cc19eba579
The netutils.ElectInterfaceAddresses function is only used in one place outside of tests: in the daemon, to configure the default bridge network. The function is also messy to reason about as it references the shared mutable state of ipamutils.PredefinedLocalScopeDefaultNetworks. It uses the list of predefined default networks to always return an IPv4 address even if the named interface does not exist or does not have any IPv4 addresses. This list happens to be the same as the one used to initialize the address pool of the 'builtin' IPAM driver, though that is far from obvious. (Start with "./libnetwork".initIPAMDrivers and trace the dataflow of the addressPool value. Surprise! Global state is being mutated using the value of other global mutable state.) The daemon does not need the fallback behaviour of ElectInterfaceAddresses. In fact, the daemon does not have to configure an address pool for the network at all! libnetwork will acquire one of the available address ranges from the network's IPAM driver when the preferred-pool configuration is unset. It will do so using the same list of address ranges and the exact same logic (netutils.FindAvailableNetworks) as ElectInterfaceAddresses. So unless the daemon needs to force the network to use a specific address range because the bridge interface already exists, it can leave the details up to libnetwork. Signed-off-by: Cory Snider <csnider@mirantis.com>
184 lines
4.8 KiB
Go
184 lines
4.8 KiB
Go
package daemon // import "github.com/docker/docker/daemon"
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/docker/docker/daemon/config"
|
|
"github.com/docker/docker/libnetwork/ns"
|
|
"github.com/docker/docker/libnetwork/resolvconf"
|
|
"github.com/moby/sys/mount"
|
|
"github.com/moby/sys/mountinfo"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/vishvananda/netlink"
|
|
)
|
|
|
|
// On Linux, plugins use a static path for storing execution state,
|
|
// instead of deriving path from daemon's exec-root. This is because
|
|
// plugin socket files are created here and they cannot exceed max
|
|
// path length of 108 bytes.
|
|
func getPluginExecRoot(_ *config.Config) string {
|
|
return "/run/docker/plugins"
|
|
}
|
|
|
|
func (daemon *Daemon) cleanupMountsByID(id string) error {
|
|
logrus.Debugf("Cleaning up old mountid %s: start.", id)
|
|
f, err := os.Open("/proc/self/mountinfo")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
|
|
return daemon.cleanupMountsFromReaderByID(f, id, mount.Unmount)
|
|
}
|
|
|
|
func (daemon *Daemon) cleanupMountsFromReaderByID(reader io.Reader, id string, unmount func(target string) error) error {
|
|
if daemon.root == "" {
|
|
return nil
|
|
}
|
|
var errs []string
|
|
|
|
regexps := getCleanPatterns(id)
|
|
sc := bufio.NewScanner(reader)
|
|
for sc.Scan() {
|
|
if fields := strings.Fields(sc.Text()); len(fields) > 4 {
|
|
if mnt := fields[4]; strings.HasPrefix(mnt, daemon.root) {
|
|
for _, p := range regexps {
|
|
if p.MatchString(mnt) {
|
|
if err := unmount(mnt); err != nil {
|
|
logrus.Error(err)
|
|
errs = append(errs, err.Error())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if err := sc.Err(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(errs) > 0 {
|
|
return fmt.Errorf("Error cleaning up mounts:\n%v", strings.Join(errs, "\n"))
|
|
}
|
|
|
|
logrus.Debugf("Cleaning up old mountid %v: done.", id)
|
|
return nil
|
|
}
|
|
|
|
// cleanupMounts umounts used by container resources and the daemon root mount
|
|
func (daemon *Daemon) cleanupMounts() error {
|
|
if err := daemon.cleanupMountsByID(""); err != nil {
|
|
return err
|
|
}
|
|
|
|
info, err := mountinfo.GetMounts(mountinfo.SingleEntryFilter(daemon.root))
|
|
if err != nil {
|
|
return errors.Wrap(err, "error reading mount table for cleanup")
|
|
}
|
|
|
|
if len(info) < 1 {
|
|
// no mount found, we're done here
|
|
return nil
|
|
}
|
|
|
|
// `info.Root` here is the root mountpoint of the passed in path (`daemon.root`).
|
|
// The ony cases that need to be cleaned up is when the daemon has performed a
|
|
// `mount --bind /daemon/root /daemon/root && mount --make-shared /daemon/root`
|
|
// This is only done when the daemon is started up and `/daemon/root` is not
|
|
// already on a shared mountpoint.
|
|
if !shouldUnmountRoot(daemon.root, info[0]) {
|
|
return nil
|
|
}
|
|
|
|
unmountFile := getUnmountOnShutdownPath(daemon.configStore)
|
|
if _, err := os.Stat(unmountFile); err != nil {
|
|
return nil
|
|
}
|
|
|
|
logrus.WithField("mountpoint", daemon.root).Debug("unmounting daemon root")
|
|
if err := mount.Unmount(daemon.root); err != nil {
|
|
return err
|
|
}
|
|
return os.Remove(unmountFile)
|
|
}
|
|
|
|
func getCleanPatterns(id string) (regexps []*regexp.Regexp) {
|
|
var patterns []string
|
|
if id == "" {
|
|
id = "[0-9a-f]{64}"
|
|
patterns = append(patterns, "containers/"+id+"/mounts/shm", "containers/"+id+"/shm")
|
|
}
|
|
patterns = append(patterns, "overlay2/"+id+"/merged$", "aufs/mnt/"+id+"$", "overlay/"+id+"/merged$", "zfs/graph/"+id+"$")
|
|
for _, p := range patterns {
|
|
r, err := regexp.Compile(p)
|
|
if err == nil {
|
|
regexps = append(regexps, r)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func shouldUnmountRoot(root string, info *mountinfo.Info) bool {
|
|
if !strings.HasSuffix(root, info.Root) {
|
|
return false
|
|
}
|
|
return hasMountInfoOption(info.Optional, sharedPropagationOption)
|
|
}
|
|
|
|
// setupResolvConf sets the appropriate resolv.conf file if not specified
|
|
// When systemd-resolved is running the default /etc/resolv.conf points to
|
|
// localhost. In this case fetch the alternative config file that is in a
|
|
// different path so that containers can use it
|
|
// In all the other cases fallback to the default one
|
|
func setupResolvConf(config *config.Config) {
|
|
if config.ResolvConf != "" {
|
|
return
|
|
}
|
|
config.ResolvConf = resolvconf.Path()
|
|
}
|
|
|
|
// ifaceAddrs returns the IPv4 and IPv6 addresses assigned to the network
|
|
// interface with name linkName.
|
|
//
|
|
// No error is returned if the named interface does not exist.
|
|
func ifaceAddrs(linkName string) (v4, v6 []*net.IPNet, err error) {
|
|
nl := ns.NlHandle()
|
|
link, err := nl.LinkByName(linkName)
|
|
if err != nil {
|
|
if !errors.As(err, new(netlink.LinkNotFoundError)) {
|
|
return nil, nil, err
|
|
}
|
|
return nil, nil, nil
|
|
}
|
|
|
|
get := func(family int) ([]*net.IPNet, error) {
|
|
addrs, err := nl.AddrList(link, family)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ipnets := make([]*net.IPNet, len(addrs))
|
|
for i := range addrs {
|
|
ipnets[i] = addrs[i].IPNet
|
|
}
|
|
return ipnets, nil
|
|
}
|
|
|
|
v4, err = get(netlink.FAMILY_V4)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
v6, err = get(netlink.FAMILY_V6)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return v4, v6, nil
|
|
}
|