diff --git a/api/client/network.go b/api/client/network.go index 66ec8a70eb..d9ff387fce 100644 --- a/api/client/network.go +++ b/api/client/network.go @@ -5,10 +5,14 @@ import ( "encoding/json" "fmt" "io" + "net" + "strings" "text/tabwriter" "github.com/docker/docker/api/types" Cli "github.com/docker/docker/cli" + "github.com/docker/docker/daemon/network" + "github.com/docker/docker/opts" flag "github.com/docker/docker/pkg/mflag" "github.com/docker/docker/pkg/stringid" ) @@ -29,15 +33,37 @@ func (cli *DockerCli) CmdNetwork(args ...string) error { // Usage: docker network create [OPTIONS] func (cli *DockerCli) CmdNetworkCreate(args ...string) error { cmd := Cli.Subcmd("network create", []string{"NETWORK-NAME"}, "Creates a new network with a name specified by the user", false) - flDriver := cmd.String([]string{"d", "-driver"}, "", "Driver to manage the Network") + flDriver := cmd.String([]string{"d", "-driver"}, "bridge", "Driver to manage the Network") + + flIpamDriver := cmd.String([]string{"-ipam-driver"}, "default", "IP Address Management Driver") + flIpamSubnet := opts.NewListOpts(nil) + flIpamIPRange := opts.NewListOpts(nil) + flIpamGateway := opts.NewListOpts(nil) + flIpamAux := opts.NewMapOpts(nil, nil) + + cmd.Var(&flIpamSubnet, []string{"-subnet"}, "Subnet in CIDR format that represents a network segment") + cmd.Var(&flIpamIPRange, []string{"-ip-range"}, "allocate container ip from a sub-range") + cmd.Var(&flIpamGateway, []string{"-gateway"}, "ipv4 or ipv6 Gateway for the master subnet") + cmd.Var(flIpamAux, []string{"-aux-address"}, "Auxiliary ipv4 or ipv6 addresses used by network driver") + cmd.Require(flag.Exact, 1) err := cmd.ParseFlags(args, true) if err != nil { return err } + ipamCfg, err := consolidateIpam(flIpamSubnet.GetAll(), flIpamIPRange.GetAll(), flIpamGateway.GetAll(), flIpamAux.GetAll()) + if err != nil { + return err + } + // Construct network create request body - nc := types.NetworkCreate{Name: cmd.Arg(0), Driver: *flDriver, CheckDuplicate: true} + nc := types.NetworkCreate{ + Name: cmd.Arg(0), + Driver: *flDriver, + IPAM: network.IPAM{Driver: *flIpamDriver, Config: ipamCfg}, + CheckDuplicate: true, + } obj, _, err := readBody(cli.call("POST", "/networks/create", nc, nil)) if err != nil { return err @@ -104,12 +130,13 @@ func (cli *DockerCli) CmdNetworkDisconnect(args ...string) error { // // Usage: docker network ls [OPTIONS] func (cli *DockerCli) CmdNetworkLs(args ...string) error { - cmd := Cli.Subcmd("network ls", []string{""}, "Lists all the networks created by the user", false) + cmd := Cli.Subcmd("network ls", nil, "Lists networks", true) quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs") - noTrunc := cmd.Bool([]string{"", "-no-trunc"}, false, "Do not truncate the output") - nLatest := cmd.Bool([]string{"l", "-latest"}, false, "Show the latest network created") - last := cmd.Int([]string{"n"}, -1, "Show n last created networks") + noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Do not truncate the output") + + cmd.Require(flag.Exact, 0) err := cmd.ParseFlags(args, true) + if err != nil { return err } @@ -117,9 +144,6 @@ func (cli *DockerCli) CmdNetworkLs(args ...string) error { if err != nil { return err } - if *last == -1 && *nLatest { - *last = 1 - } var networkResources []types.NetworkResource err = json.Unmarshal(obj, &networkResources) @@ -186,6 +210,129 @@ func (cli *DockerCli) CmdNetworkInspect(args ...string) error { return nil } +// Consolidates the ipam configuration as a group from differnt related configurations +// user can configure network with multiple non-overlapping subnets and hence it is +// possible to corelate the various related parameters and consolidate them. +// consoidateIpam consolidates subnets, ip-ranges, gateways and auxilary addresses into +// structured ipam data. +func consolidateIpam(subnets, ranges, gateways []string, auxaddrs map[string]string) ([]network.IPAMConfig, error) { + if len(subnets) < len(ranges) || len(subnets) < len(gateways) { + return nil, fmt.Errorf("every ip-range or gateway must have a corresponding subnet") + } + iData := map[string]*network.IPAMConfig{} + + // Populate non-overlapping subnets into consolidation map + for _, s := range subnets { + for k := range iData { + ok1, err := subnetMatches(s, k) + if err != nil { + return nil, err + } + ok2, err := subnetMatches(k, s) + if err != nil { + return nil, err + } + if ok1 || ok2 { + return nil, fmt.Errorf("multiple overlapping subnet configuration is not supported") + } + } + iData[s] = &network.IPAMConfig{Subnet: s, AuxAddress: map[string]string{}} + } + + // Validate and add valid ip ranges + for _, r := range ranges { + match := false + for _, s := range subnets { + ok, err := subnetMatches(s, r) + if err != nil { + return nil, err + } + if !ok { + continue + } + if iData[s].IPRange != "" { + return nil, fmt.Errorf("cannot configure multiple ranges (%s, %s) on the same subnet (%s)", r, iData[s].IPRange, s) + } + d := iData[s] + d.IPRange = r + match = true + } + if !match { + return nil, fmt.Errorf("no matching subnet for range %s", r) + } + } + + // Validate and add valid gateways + for _, g := range gateways { + match := false + for _, s := range subnets { + ok, err := subnetMatches(s, g) + if err != nil { + return nil, err + } + if !ok { + continue + } + if iData[s].Gateway != "" { + return nil, fmt.Errorf("cannot configure multiple gateways (%s, %s) for the same subnet (%s)", g, iData[s].Gateway, s) + } + d := iData[s] + d.Gateway = g + match = true + } + if !match { + return nil, fmt.Errorf("no matching subnet for gateway %s", g) + } + } + + // Validate and add aux-addresses + for key, aa := range auxaddrs { + match := false + for _, s := range subnets { + ok, err := subnetMatches(s, aa) + if err != nil { + return nil, err + } + if !ok { + continue + } + iData[s].AuxAddress[key] = aa + match = true + } + if !match { + return nil, fmt.Errorf("no matching subnet for aux-address %s", aa) + } + } + + idl := []network.IPAMConfig{} + for _, v := range iData { + idl = append(idl, *v) + } + return idl, nil +} + +func subnetMatches(subnet, data string) (bool, error) { + var ( + ip net.IP + ) + + _, s, err := net.ParseCIDR(subnet) + if err != nil { + return false, fmt.Errorf("Invalid subnet %s : %v", s, err) + } + + if strings.Contains(data, "/") { + ip, _, err = net.ParseCIDR(data) + if err != nil { + return false, fmt.Errorf("Invalid cidr %s : %v", data, err) + } + } else { + ip = net.ParseIP(data) + } + + return s.Contains(ip), nil +} + func networkUsage() string { networkCommands := map[string]string{ "create": "Create a network", diff --git a/api/server/router/network/network_routes.go b/api/server/router/network/network_routes.go index 14cc8f1503..9d77054162 100644 --- a/api/server/router/network/network_routes.go +++ b/api/server/router/network/network_routes.go @@ -11,6 +11,7 @@ import ( "github.com/docker/docker/api/server/httputils" "github.com/docker/docker/api/types" "github.com/docker/docker/daemon" + "github.com/docker/docker/daemon/network" "github.com/docker/docker/pkg/parsers/filters" "github.com/docker/libnetwork" ) @@ -95,7 +96,7 @@ func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWr warning = fmt.Sprintf("Network with name %s (id : %s) already exists", nw.Name(), nw.ID()) } - nw, err = n.daemon.CreateNetwork(create.Name, create.Driver, create.Options) + nw, err = n.daemon.CreateNetwork(create.Name, create.Driver, create.IPAM) if err != nil { return err } @@ -179,8 +180,11 @@ func buildNetworkResource(nw libnetwork.Network) *types.NetworkResource { r.Name = nw.Name() r.ID = nw.ID() + r.Scope = nw.Info().Scope() r.Driver = nw.Type() r.Containers = make(map[string]types.EndpointResource) + buildIpamResources(r, nw) + epl := nw.Endpoints() for _, e := range epl { sb := e.Info().Sandbox() @@ -193,6 +197,31 @@ func buildNetworkResource(nw libnetwork.Network) *types.NetworkResource { return r } +func buildIpamResources(r *types.NetworkResource, nw libnetwork.Network) { + id, ipv4conf, ipv6conf := nw.Info().IpamConfig() + + r.IPAM.Driver = id + + r.IPAM.Config = []network.IPAMConfig{} + for _, ip4 := range ipv4conf { + iData := network.IPAMConfig{} + iData.Subnet = ip4.PreferredPool + iData.IPRange = ip4.SubPool + iData.Gateway = ip4.Gateway + iData.AuxAddress = ip4.AuxAddresses + r.IPAM.Config = append(r.IPAM.Config, iData) + } + + for _, ip6 := range ipv6conf { + iData := network.IPAMConfig{} + iData.Subnet = ip6.PreferredPool + iData.IPRange = ip6.SubPool + iData.Gateway = ip6.Gateway + iData.AuxAddress = ip6.AuxAddresses + r.IPAM.Config = append(r.IPAM.Config, iData) + } +} + func buildEndpointResource(e libnetwork.Endpoint) types.EndpointResource { er := types.EndpointResource{} if e == nil { @@ -204,12 +233,12 @@ func buildEndpointResource(e libnetwork.Endpoint) types.EndpointResource { if mac := iface.MacAddress(); mac != nil { er.MacAddress = mac.String() } - if ip := iface.Address(); len(ip.IP) > 0 { - er.IPv4Address = (&ip).String() + if ip := iface.Address(); ip != nil && len(ip.IP) > 0 { + er.IPv4Address = ip.String() } - if ipv6 := iface.AddressIPv6(); len(ipv6.IP) > 0 { - er.IPv6Address = (&ipv6).String() + if ipv6 := iface.AddressIPv6(); ipv6 != nil && len(ipv6.IP) > 0 { + er.IPv6Address = ipv6.String() } } return er diff --git a/api/types/types.go b/api/types/types.go index a3a61a10b7..ed9a0831a7 100644 --- a/api/types/types.go +++ b/api/types/types.go @@ -315,9 +315,10 @@ type VolumeCreateRequest struct { type NetworkResource struct { Name string `json:"name"` ID string `json:"id"` + Scope string `json:"scope"` Driver string `json:"driver"` + IPAM network.IPAM `json:"ipam"` Containers map[string]EndpointResource `json:"containers"` - Options map[string]interface{} `json:"options,omitempty"` } //EndpointResource contains network resources allocated and usd for a container in a network @@ -330,10 +331,10 @@ type EndpointResource struct { // NetworkCreate is the expected body of the "create network" http request message type NetworkCreate struct { - Name string `json:"name"` - CheckDuplicate bool `json:"check_duplicate"` - Driver string `json:"driver"` - Options map[string]interface{} `json:"options"` + Name string `json:"name"` + CheckDuplicate bool `json:"check_duplicate"` + Driver string `json:"driver"` + IPAM network.IPAM `json:"ipam"` } // NetworkCreateResponse is the response message sent by the server for network create call diff --git a/daemon/container_unix.go b/daemon/container_unix.go index a31fde6f78..ef708c104e 100644 --- a/daemon/container_unix.go +++ b/daemon/container_unix.go @@ -30,6 +30,7 @@ import ( "github.com/docker/docker/volume" "github.com/docker/docker/volume/store" "github.com/docker/libnetwork" + "github.com/docker/libnetwork/drivers/bridge" "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/options" "github.com/docker/libnetwork/types" @@ -651,11 +652,13 @@ func (container *Container) buildEndpointInfo(ep libnetwork.Endpoint, networkSet return networkSettings, nil } - ones, _ := iface.Address().Mask.Size() - networkSettings.IPAddress = iface.Address().IP.String() - networkSettings.IPPrefixLen = ones + if iface.Address() != nil { + ones, _ := iface.Address().Mask.Size() + networkSettings.IPAddress = iface.Address().IP.String() + networkSettings.IPPrefixLen = ones + } - if iface.AddressIPv6().IP.To16() != nil { + if iface.AddressIPv6() != nil && iface.AddressIPv6().IP.To16() != nil { onesv6, _ := iface.AddressIPv6().Mask.Size() networkSettings.GlobalIPv6Address = iface.AddressIPv6().IP.String() networkSettings.GlobalIPv6PrefixLen = onesv6 @@ -861,9 +864,8 @@ func createNetwork(controller libnetwork.NetworkController, dnet string, driver // Bridge driver is special due to legacy reasons if runconfig.NetworkMode(driver).IsBridge() { - genericOption[netlabel.GenericData] = map[string]interface{}{ - "BridgeName": dnet, - "AllowNonDefaultBridge": "true", + genericOption[netlabel.GenericData] = map[string]string{ + bridge.BridgeName: dnet, } networkOption := libnetwork.NetworkOptionGeneric(genericOption) createOptions = append(createOptions, networkOption) @@ -1163,7 +1165,7 @@ func (container *Container) disconnectFromNetwork(n libnetwork.Network, updateSe n.WalkEndpoints(s) if ep == nil { - return fmt.Errorf("could not locate network endpoint for container %s", container.ID) + return fmt.Errorf("container %s is not connected to the network", container.ID) } if err := ep.Leave(sbox); err != nil { diff --git a/daemon/daemon_unix.go b/daemon/daemon_unix.go index d251d070c7..3e6c7a6a43 100644 --- a/daemon/daemon_unix.go +++ b/daemon/daemon_unix.go @@ -7,6 +7,7 @@ import ( "net" "os" "path/filepath" + "strconv" "strings" "syscall" @@ -23,8 +24,11 @@ import ( "github.com/docker/docker/utils" "github.com/docker/libnetwork" nwconfig "github.com/docker/libnetwork/config" + "github.com/docker/libnetwork/drivers/bridge" + "github.com/docker/libnetwork/ipamutils" "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/options" + "github.com/docker/libnetwork/types" "github.com/opencontainers/runc/libcontainer/label" "github.com/vishvananda/netlink" ) @@ -312,6 +316,9 @@ func (daemon *Daemon) networkOptions(dconfig *Config) ([]nwconfig.Option, error) if dconfig == nil { return options, nil } + + options = append(options, nwconfig.OptionDataDir(dconfig.Root)) + if strings.TrimSpace(dconfig.DefaultNetwork) != "" { dn := strings.Split(dconfig.DefaultNetwork, ":") if len(dn) < 2 { @@ -392,22 +399,48 @@ func driverOptions(config *Config) []nwconfig.Option { } func initBridgeDriver(controller libnetwork.NetworkController, config *Config) error { - netOption := options.Generic{ - "BridgeName": config.Bridge.Iface, - "DefaultBridge": true, - "Mtu": config.Mtu, - "EnableIPMasquerade": config.Bridge.EnableIPMasq, - "EnableICC": config.Bridge.InterContainerCommunication, + if n, err := controller.NetworkByName("bridge"); err == nil { + if err = n.Delete(); err != nil { + return fmt.Errorf("could not delete the default bridge network: %v", err) + } + } + + bridgeName := bridge.DefaultBridgeName + if config.Bridge.Iface != "" { + bridgeName = config.Bridge.Iface + } + netOption := map[string]string{ + bridge.BridgeName: bridgeName, + bridge.DefaultBridge: strconv.FormatBool(true), + netlabel.DriverMTU: strconv.Itoa(config.Mtu), + bridge.EnableIPMasquerade: strconv.FormatBool(config.Bridge.EnableIPMasq), + bridge.EnableICC: strconv.FormatBool(config.Bridge.InterContainerCommunication), + } + + // --ip processing + if config.Bridge.DefaultIP != nil { + netOption[bridge.DefaultBindingIP] = config.Bridge.DefaultIP.String() + } + + ipamV4Conf := libnetwork.IpamConf{} + + ipamV4Conf.AuxAddresses = make(map[string]string) + + if nw, _, err := ipamutils.ElectInterfaceAddresses(bridgeName); err == nil { + ipamV4Conf.PreferredPool = nw.String() + hip, _ := types.GetHostPartIP(nw.IP, nw.Mask) + if hip.IsGlobalUnicast() { + ipamV4Conf.Gateway = nw.IP.String() + } } if config.Bridge.IP != "" { - ip, bipNet, err := net.ParseCIDR(config.Bridge.IP) + ipamV4Conf.PreferredPool = config.Bridge.IP + ip, _, err := net.ParseCIDR(config.Bridge.IP) if err != nil { return err } - - bipNet.IP = ip - netOption["AddressIPv4"] = bipNet + ipamV4Conf.Gateway = ip.String() } if config.Bridge.FixedCIDR != "" { @@ -416,38 +449,44 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *Config) e return err } - netOption["FixedCIDR"] = fCIDR + ipamV4Conf.SubPool = fCIDR.String() } + if config.Bridge.DefaultGatewayIPv4 != nil { + ipamV4Conf.AuxAddresses["DefaultGatewayIPv4"] = config.Bridge.DefaultGatewayIPv4.String() + } + + var ipamV6Conf *libnetwork.IpamConf if config.Bridge.FixedCIDRv6 != "" { _, fCIDRv6, err := net.ParseCIDR(config.Bridge.FixedCIDRv6) if err != nil { return err } - - netOption["FixedCIDRv6"] = fCIDRv6 - } - - if config.Bridge.DefaultGatewayIPv4 != nil { - netOption["DefaultGatewayIPv4"] = config.Bridge.DefaultGatewayIPv4 + if ipamV6Conf == nil { + ipamV6Conf = &libnetwork.IpamConf{} + } + ipamV6Conf.PreferredPool = fCIDRv6.String() } if config.Bridge.DefaultGatewayIPv6 != nil { - netOption["DefaultGatewayIPv6"] = config.Bridge.DefaultGatewayIPv6 + if ipamV6Conf == nil { + ipamV6Conf = &libnetwork.IpamConf{} + } + ipamV6Conf.AuxAddresses["DefaultGatewayIPv6"] = config.Bridge.DefaultGatewayIPv6.String() } - // --ip processing - if config.Bridge.DefaultIP != nil { - netOption["DefaultBindingIP"] = config.Bridge.DefaultIP + v4Conf := []*libnetwork.IpamConf{&ipamV4Conf} + v6Conf := []*libnetwork.IpamConf{} + if ipamV6Conf != nil { + v6Conf = append(v6Conf, ipamV6Conf) } - // Initialize default network on "bridge" with the same name _, err := controller.NewNetwork("bridge", "bridge", libnetwork.NetworkOptionGeneric(options.Generic{ netlabel.GenericData: netOption, netlabel.EnableIPv6: config.Bridge.EnableIPv6, }), - libnetwork.NetworkOptionPersist(false)) + libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf)) if err != nil { return fmt.Errorf("Error creating default \"bridge\" network: %v", err) } diff --git a/daemon/network.go b/daemon/network.go index 546828a4c3..2cc1647108 100644 --- a/daemon/network.go +++ b/daemon/network.go @@ -2,10 +2,12 @@ package daemon import ( "errors" + "fmt" + "net" "strings" + "github.com/docker/docker/daemon/network" "github.com/docker/libnetwork" - "github.com/docker/libnetwork/netlabel" ) const ( @@ -78,29 +80,43 @@ func (daemon *Daemon) GetNetworksByID(partialID string) []libnetwork.Network { } // CreateNetwork creates a network with the given name, driver and other optional parameters -func (daemon *Daemon) CreateNetwork(name, driver string, options map[string]interface{}) (libnetwork.Network, error) { +func (daemon *Daemon) CreateNetwork(name, driver string, ipam network.IPAM) (libnetwork.Network, error) { c := daemon.netController if driver == "" { driver = c.Config().Daemon.DefaultDriver } - if options == nil { - options = make(map[string]interface{}) - } - _, ok := options[netlabel.GenericData] - if !ok { - options[netlabel.GenericData] = make(map[string]interface{}) + nwOptions := []libnetwork.NetworkOption{} + + v4Conf, v6Conf, err := getIpamConfig(ipam.Config) + if err != nil { + return nil, err } - return c.NewNetwork(driver, name, parseOptions(options)...) + if len(ipam.Config) > 0 { + nwOptions = append(nwOptions, libnetwork.NetworkOptionIpam(ipam.Driver, "", v4Conf, v6Conf)) + } + return c.NewNetwork(driver, name, nwOptions...) } -func parseOptions(options map[string]interface{}) []libnetwork.NetworkOption { - var setFctList []libnetwork.NetworkOption - - if options != nil { - setFctList = append(setFctList, libnetwork.NetworkOptionGeneric(options)) +func getIpamConfig(data []network.IPAMConfig) ([]*libnetwork.IpamConf, []*libnetwork.IpamConf, error) { + ipamV4Cfg := []*libnetwork.IpamConf{} + ipamV6Cfg := []*libnetwork.IpamConf{} + for _, d := range data { + iCfg := libnetwork.IpamConf{} + iCfg.PreferredPool = d.Subnet + iCfg.SubPool = d.IPRange + iCfg.Gateway = d.Gateway + iCfg.AuxAddresses = d.AuxAddress + ip, _, err := net.ParseCIDR(d.Subnet) + if err != nil { + return nil, nil, fmt.Errorf("Invalid subnet %s : %v", d.Subnet, err) + } + if ip.To4() != nil { + ipamV4Cfg = append(ipamV4Cfg, &iCfg) + } else { + ipamV6Cfg = append(ipamV6Cfg, &iCfg) + } } - - return setFctList + return ipamV4Cfg, ipamV6Cfg, nil } diff --git a/daemon/network/settings.go b/daemon/network/settings.go index c88a06c0a7..d59c369db9 100644 --- a/daemon/network/settings.go +++ b/daemon/network/settings.go @@ -8,6 +8,20 @@ type Address struct { PrefixLen int } +// IPAM represents IP Address Management +type IPAM struct { + Driver string `json:"driver"` + Config []IPAMConfig `json:"config"` +} + +// IPAMConfig represents IPAM configurations +type IPAMConfig struct { + Subnet string `json:"subnet,omitempty"` + IPRange string `json:"ip_range,omitempty"` + Gateway string `json:"gateway,omitempty"` + AuxAddress map[string]string `json:"auxiliary_address,omitempty"` +} + // Settings stores configuration details about the daemon network config // TODO Windows. Many of these fields can be factored out., type Settings struct { diff --git a/daemon/stats.go b/daemon/stats.go index 74d5bfc1d0..ae3a2d918b 100644 --- a/daemon/stats.go +++ b/daemon/stats.go @@ -8,7 +8,7 @@ import ( "github.com/docker/docker/api/types/versions/v1p20" "github.com/docker/docker/daemon/execdriver" "github.com/docker/docker/pkg/version" - "github.com/docker/libnetwork/osl" + lntypes "github.com/docker/libnetwork/types" "github.com/opencontainers/runc/libcontainer" ) @@ -166,7 +166,7 @@ func (daemon *Daemon) getNetworkStats(c *Container) ([]*libcontainer.NetworkInte return list, nil } -func convertLnNetworkStats(name string, stats *osl.InterfaceStatistics) *libcontainer.NetworkInterface { +func convertLnNetworkStats(name string, stats *lntypes.InterfaceStatistics) *libcontainer.NetworkInterface { n := &libcontainer.NetworkInterface{Name: name} n.RxBytes = stats.RxBytes n.RxPackets = stats.RxPackets diff --git a/docker/flags_experimental.go b/docker/flags_experimental.go deleted file mode 100644 index 0994a82dd0..0000000000 --- a/docker/flags_experimental.go +++ /dev/null @@ -1,16 +0,0 @@ -// +build experimental - -package main - -import ( - "sort" - - "github.com/docker/docker/cli" -) - -func init() { - dockerCommands = append(dockerCommands, cli.Command{Name: "network", Description: "Network management"}) - - //Sorting logic required here to pass Command Sort Test. - sort.Sort(byName(dockerCommands)) -} diff --git a/hack/vendor.sh b/hack/vendor.sh index 02fa32706f..6b3c42f3f9 100755 --- a/hack/vendor.sh +++ b/hack/vendor.sh @@ -20,12 +20,12 @@ clone git github.com/tchap/go-patricia v2.1.0 clone git golang.org/x/net 3cffabab72adf04f8e3b01c5baf775361837b5fe https://github.com/golang/net.git #get libnetwork packages -clone git github.com/docker/libnetwork c3a9e0d8d0c53f3db251620e5f48470e267f292b +clone git github.com/docker/libnetwork 0521fe53fc3e7d4e7c2e463800f36662c9169f20 clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4 clone git github.com/hashicorp/serf 7151adcef72687bf95f451a2e0ba15cb19412bf2 -clone git github.com/docker/libkv ea7ff6ae76485ab93ac36799d3e13b1905787ffe +clone git github.com/docker/libkv 958cd316db2bc916466bdcc632a3188d62ce6e87 clone git github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25 clone git github.com/vishvananda/netlink 4b5dce31de6d42af5bb9811c6d265472199e0fec clone git github.com/BurntSushi/toml f706d00e3de6abe700c994cdd545a1a4915af060 diff --git a/integration-cli/docker_api_network_test.go b/integration-cli/docker_api_network_test.go index 587cb363ad..e668bb7594 100644 --- a/integration-cli/docker_api_network_test.go +++ b/integration-cli/docker_api_network_test.go @@ -2,12 +2,14 @@ package main import ( "encoding/json" + "fmt" "net" "net/http" "net/url" "strings" "github.com/docker/docker/api/types" + "github.com/docker/docker/daemon/network" "github.com/docker/docker/pkg/parsers/filters" "github.com/go-check/check" ) @@ -23,11 +25,15 @@ func (s *DockerSuite) TestApiNetworkGetDefaults(c *check.C) { func (s *DockerSuite) TestApiNetworkCreateDelete(c *check.C) { // Create a network name := "testnetwork" - id := createNetwork(c, name, true) + config := types.NetworkCreate{ + Name: name, + CheckDuplicate: true, + } + id := createNetwork(c, config, true) c.Assert(isNetworkAvailable(c, name), check.Equals, true) // POST another network with same name and CheckDuplicate must fail - createNetwork(c, name, false) + createNetwork(c, config, false) // delete the network and make sure it is deleted deleteNetwork(c, id, true) @@ -51,18 +57,46 @@ func (s *DockerSuite) TestApiNetworkInspect(c *check.C) { // inspect default bridge network again and make sure the container is connected nr = getNetworkResource(c, nr.ID) + c.Assert(nr.Driver, check.Equals, "bridge") + c.Assert(nr.Scope, check.Equals, "local") + c.Assert(nr.IPAM.Driver, check.Equals, "default") c.Assert(len(nr.Containers), check.Equals, 1) c.Assert(nr.Containers[containerID], check.NotNil) ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address) c.Assert(err, check.IsNil) c.Assert(ip.String(), check.Equals, containerIP) + + // IPAM configuration inspect + ipam := network.IPAM{ + Driver: "default", + Config: []network.IPAMConfig{{Subnet: "172.28.0.0/16", IPRange: "172.28.5.0/24", Gateway: "172.28.5.254"}}, + } + config := types.NetworkCreate{ + Name: "br0", + Driver: "bridge", + IPAM: ipam, + } + id0 := createNetwork(c, config, true) + c.Assert(isNetworkAvailable(c, "br0"), check.Equals, true) + + nr = getNetworkResource(c, id0) + c.Assert(len(nr.IPAM.Config), check.Equals, 1) + c.Assert(nr.IPAM.Config[0].Subnet, check.Equals, "172.28.0.0/16") + c.Assert(nr.IPAM.Config[0].IPRange, check.Equals, "172.28.5.0/24") + c.Assert(nr.IPAM.Config[0].Gateway, check.Equals, "172.28.5.254") + // delete the network and make sure it is deleted + deleteNetwork(c, id0, true) + c.Assert(isNetworkAvailable(c, "br0"), check.Equals, false) } func (s *DockerSuite) TestApiNetworkConnectDisconnect(c *check.C) { // Create test network name := "testnetwork" - id := createNetwork(c, name, true) + config := types.NetworkCreate{ + Name: name, + } + id := createNetwork(c, config, true) nr := getNetworkResource(c, id) c.Assert(nr.Name, check.Equals, name) c.Assert(nr.ID, check.Equals, id) @@ -96,6 +130,64 @@ func (s *DockerSuite) TestApiNetworkConnectDisconnect(c *check.C) { deleteNetwork(c, nr.ID, true) } +func (s *DockerSuite) TestApiNetworkIpamMultipleBridgeNetworks(c *check.C) { + // test0 bridge network + ipam0 := network.IPAM{ + Driver: "default", + Config: []network.IPAMConfig{{Subnet: "192.178.0.0/16", IPRange: "192.178.128.0/17", Gateway: "192.178.138.100"}}, + } + config0 := types.NetworkCreate{ + Name: "test0", + Driver: "bridge", + IPAM: ipam0, + } + id0 := createNetwork(c, config0, true) + c.Assert(isNetworkAvailable(c, "test0"), check.Equals, true) + + ipam1 := network.IPAM{ + Driver: "default", + Config: []network.IPAMConfig{{Subnet: "192.178.128.0/17", Gateway: "192.178.128.1"}}, + } + // test1 bridge network overlaps with test0 + config1 := types.NetworkCreate{ + Name: "test1", + Driver: "bridge", + IPAM: ipam1, + } + createNetwork(c, config1, false) + c.Assert(isNetworkAvailable(c, "test1"), check.Equals, false) + + ipam2 := network.IPAM{ + Driver: "default", + Config: []network.IPAMConfig{{Subnet: "192.169.0.0/16", Gateway: "192.169.100.100"}}, + } + // test2 bridge network does not overlap + config2 := types.NetworkCreate{ + Name: "test2", + Driver: "bridge", + IPAM: ipam2, + } + createNetwork(c, config2, true) + c.Assert(isNetworkAvailable(c, "test2"), check.Equals, true) + + // remove test0 and retry to create test1 + deleteNetwork(c, id0, true) + createNetwork(c, config1, true) + c.Assert(isNetworkAvailable(c, "test1"), check.Equals, true) + + // for networks w/o ipam specified, docker will choose proper non-overlapping subnets + createNetwork(c, types.NetworkCreate{Name: "test3"}, true) + c.Assert(isNetworkAvailable(c, "test3"), check.Equals, true) + createNetwork(c, types.NetworkCreate{Name: "test4"}, true) + c.Assert(isNetworkAvailable(c, "test4"), check.Equals, true) + createNetwork(c, types.NetworkCreate{Name: "test5"}, true) + c.Assert(isNetworkAvailable(c, "test5"), check.Equals, true) + + for i := 1; i < 6; i++ { + deleteNetwork(c, fmt.Sprintf("test%d", i), true) + } +} + func isNetworkAvailable(c *check.C, name string) bool { status, body, err := sockRequest("GET", "/networks", nil) c.Assert(status, check.Equals, http.StatusOK) @@ -146,13 +238,7 @@ func getNetworkResource(c *check.C, id string) *types.NetworkResource { return &nr } -func createNetwork(c *check.C, name string, shouldSucceed bool) string { - config := types.NetworkCreate{ - Name: name, - Driver: "bridge", - CheckDuplicate: true, - } - +func createNetwork(c *check.C, config types.NetworkCreate, shouldSucceed bool) string { status, resp, err := sockRequest("POST", "/networks/create", config) if !shouldSucceed { c.Assert(status, check.Not(check.Equals), http.StatusCreated) diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index 451027ccce..6567809bfa 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -787,7 +787,7 @@ func (s *DockerDaemonSuite) TestDaemonBridgeFixedCidr(c *check.C) { cName := "Container" + strconv.Itoa(i) out, err := d.Cmd("run", "-d", "--name", cName, "busybox", "top") if err != nil { - c.Assert(strings.Contains(out, "no available ip addresses"), check.Equals, true, + c.Assert(strings.Contains(out, "no available IPv4 addresses"), check.Equals, true, check.Commentf("Could not run a Container : %s %s", err.Error(), out)) } } diff --git a/integration-cli/docker_cli_network_test.go b/integration-cli/docker_cli_network_test.go deleted file mode 100644 index 134133918d..0000000000 --- a/integration-cli/docker_cli_network_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package main - -import ( - "encoding/json" - "net" - "strings" - - "github.com/docker/docker/api/types" - "github.com/go-check/check" -) - -func assertNwIsAvailable(c *check.C, name string) { - if !isNwPresent(c, name) { - c.Fatalf("Network %s not found in network ls o/p", name) - } -} - -func assertNwNotAvailable(c *check.C, name string) { - if isNwPresent(c, name) { - c.Fatalf("Found network %s in network ls o/p", name) - } -} - -func isNwPresent(c *check.C, name string) bool { - out, _ := dockerCmd(c, "network", "ls") - lines := strings.Split(out, "\n") - for i := 1; i < len(lines)-1; i++ { - if strings.Contains(lines[i], name) { - return true - } - } - return false -} - -func getNwResource(c *check.C, name string) *types.NetworkResource { - out, _ := dockerCmd(c, "network", "inspect", name) - nr := types.NetworkResource{} - err := json.Unmarshal([]byte(out), &nr) - c.Assert(err, check.IsNil) - return &nr -} - -func (s *DockerSuite) TestDockerNetworkLsDefault(c *check.C) { - defaults := []string{"bridge", "host", "none"} - for _, nn := range defaults { - assertNwIsAvailable(c, nn) - } -} - -func (s *DockerSuite) TestDockerNetworkCreateDelete(c *check.C) { - dockerCmd(c, "network", "create", "test") - assertNwIsAvailable(c, "test") - - dockerCmd(c, "network", "rm", "test") - assertNwNotAvailable(c, "test") -} - -func (s *DockerSuite) TestDockerNetworkConnectDisconnect(c *check.C) { - dockerCmd(c, "network", "create", "test") - assertNwIsAvailable(c, "test") - nr := getNwResource(c, "test") - - c.Assert(nr.Name, check.Equals, "test") - c.Assert(len(nr.Containers), check.Equals, 0) - - // run a container - out, _ := dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top") - c.Assert(waitRun("test"), check.IsNil) - containerID := strings.TrimSpace(out) - - // connect the container to the test network - dockerCmd(c, "network", "connect", "test", containerID) - - // inspect the network to make sure container is connected - nr = getNetworkResource(c, nr.ID) - c.Assert(len(nr.Containers), check.Equals, 1) - c.Assert(nr.Containers[containerID], check.NotNil) - - // check if container IP matches network inspect - ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address) - c.Assert(err, check.IsNil) - containerIP := findContainerIP(c, "test") - c.Assert(ip.String(), check.Equals, containerIP) - - // disconnect container from the network - dockerCmd(c, "network", "disconnect", "test", containerID) - nr = getNwResource(c, "test") - c.Assert(nr.Name, check.Equals, "test") - c.Assert(len(nr.Containers), check.Equals, 0) - - // check if network connect fails for inactive containers - dockerCmd(c, "stop", containerID) - _, _, err = dockerCmdWithError("network", "connect", "test", containerID) - c.Assert(err, check.NotNil) - - dockerCmd(c, "network", "rm", "test") - assertNwNotAvailable(c, "test") -} diff --git a/integration-cli/docker_cli_network_unix_test.go b/integration-cli/docker_cli_network_unix_test.go new file mode 100644 index 0000000000..c25bd8840f --- /dev/null +++ b/integration-cli/docker_cli_network_unix_test.go @@ -0,0 +1,257 @@ +// +build !windows + +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net" + "net/http" + "net/http/httptest" + "os" + "strings" + + "github.com/docker/docker/api/types" + "github.com/docker/libnetwork/driverapi" + "github.com/go-check/check" +) + +const dummyNetworkDriver = "dummy-network-driver" + +func init() { + check.Suite(&DockerNetworkSuite{ + ds: &DockerSuite{}, + }) +} + +type DockerNetworkSuite struct { + server *httptest.Server + ds *DockerSuite + d *Daemon +} + +func (s *DockerNetworkSuite) SetUpTest(c *check.C) { + s.d = NewDaemon(c) +} + +func (s *DockerNetworkSuite) TearDownTest(c *check.C) { + s.d.Stop() + s.ds.TearDownTest(c) +} + +func (s *DockerNetworkSuite) SetUpSuite(c *check.C) { + mux := http.NewServeMux() + s.server = httptest.NewServer(mux) + if s.server == nil { + c.Fatal("Failed to start a HTTP Server") + } + + mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") + fmt.Fprintf(w, `{"Implements": ["%s"]}`, driverapi.NetworkPluginEndpointType) + }) + + mux.HandleFunc(fmt.Sprintf("/%s.GetCapabilities", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") + fmt.Fprintf(w, `{"Scope":"local"}`) + }) + + mux.HandleFunc(fmt.Sprintf("/%s.CreateNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") + fmt.Fprintf(w, "null") + }) + + mux.HandleFunc(fmt.Sprintf("/%s.DeleteNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") + fmt.Fprintf(w, "null") + }) + + if err := os.MkdirAll("/etc/docker/plugins", 0755); err != nil { + c.Fatal(err) + } + + fileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", dummyNetworkDriver) + if err := ioutil.WriteFile(fileName, []byte(s.server.URL), 0644); err != nil { + c.Fatal(err) + } +} + +func (s *DockerNetworkSuite) TearDownSuite(c *check.C) { + if s.server == nil { + return + } + + s.server.Close() + + if err := os.RemoveAll("/etc/docker/plugins"); err != nil { + c.Fatal(err) + } +} + +func assertNwIsAvailable(c *check.C, name string) { + if !isNwPresent(c, name) { + c.Fatalf("Network %s not found in network ls o/p", name) + } +} + +func assertNwNotAvailable(c *check.C, name string) { + if isNwPresent(c, name) { + c.Fatalf("Found network %s in network ls o/p", name) + } +} + +func isNwPresent(c *check.C, name string) bool { + out, _ := dockerCmd(c, "network", "ls") + lines := strings.Split(out, "\n") + for i := 1; i < len(lines)-1; i++ { + if strings.Contains(lines[i], name) { + return true + } + } + return false +} + +func getNwResource(c *check.C, name string) *types.NetworkResource { + out, _ := dockerCmd(c, "network", "inspect", name) + nr := types.NetworkResource{} + err := json.Unmarshal([]byte(out), &nr) + c.Assert(err, check.IsNil) + return &nr +} + +func (s *DockerNetworkSuite) TestDockerNetworkLsDefault(c *check.C) { + defaults := []string{"bridge", "host", "none"} + for _, nn := range defaults { + assertNwIsAvailable(c, nn) + } +} + +func (s *DockerNetworkSuite) TestDockerNetworkCreateDelete(c *check.C) { + dockerCmd(c, "network", "create", "test") + assertNwIsAvailable(c, "test") + + dockerCmd(c, "network", "rm", "test") + assertNwNotAvailable(c, "test") +} + +func (s *DockerNetworkSuite) TestDockerNetworkConnectDisconnect(c *check.C) { + dockerCmd(c, "network", "create", "test") + assertNwIsAvailable(c, "test") + nr := getNwResource(c, "test") + + c.Assert(nr.Name, check.Equals, "test") + c.Assert(len(nr.Containers), check.Equals, 0) + + // run a container + out, _ := dockerCmd(c, "run", "-d", "--name", "test", "busybox", "top") + c.Assert(waitRun("test"), check.IsNil) + containerID := strings.TrimSpace(out) + + // connect the container to the test network + dockerCmd(c, "network", "connect", "test", containerID) + + // inspect the network to make sure container is connected + nr = getNetworkResource(c, nr.ID) + c.Assert(len(nr.Containers), check.Equals, 1) + c.Assert(nr.Containers[containerID], check.NotNil) + + // check if container IP matches network inspect + ip, _, err := net.ParseCIDR(nr.Containers[containerID].IPv4Address) + c.Assert(err, check.IsNil) + containerIP := findContainerIP(c, "test") + c.Assert(ip.String(), check.Equals, containerIP) + + // disconnect container from the network + dockerCmd(c, "network", "disconnect", "test", containerID) + nr = getNwResource(c, "test") + c.Assert(nr.Name, check.Equals, "test") + c.Assert(len(nr.Containers), check.Equals, 0) + + // check if network connect fails for inactive containers + dockerCmd(c, "stop", containerID) + _, _, err = dockerCmdWithError("network", "connect", "test", containerID) + c.Assert(err, check.NotNil) + + dockerCmd(c, "network", "rm", "test") + assertNwNotAvailable(c, "test") +} + +func (s *DockerNetworkSuite) TestDockerNetworkIpamMultipleNetworks(c *check.C) { + // test0 bridge network + dockerCmd(c, "network", "create", "--subnet=192.168.0.0/16", "test1") + assertNwIsAvailable(c, "test1") + + // test2 bridge network does not overlap + dockerCmd(c, "network", "create", "--subnet=192.169.0.0/16", "test2") + assertNwIsAvailable(c, "test2") + + // for networks w/o ipam specified, docker will choose proper non-overlapping subnets + dockerCmd(c, "network", "create", "test3") + assertNwIsAvailable(c, "test3") + dockerCmd(c, "network", "create", "test4") + assertNwIsAvailable(c, "test4") + dockerCmd(c, "network", "create", "test5") + assertNwIsAvailable(c, "test5") + + // test network with multiple subnets + // bridge network doesnt support multiple subnets. hence, use a dummy driver that supports + + dockerCmd(c, "network", "create", "-d", dummyNetworkDriver, "--subnet=192.168.0.0/16", "--subnet=192.170.0.0/16", "test6") + assertNwIsAvailable(c, "test6") + + // test network with multiple subnets with valid ipam combinations + // also check same subnet across networks when the driver supports it. + dockerCmd(c, "network", "create", "-d", dummyNetworkDriver, + "--subnet=192.168.0.0/16", "--subnet=192.170.0.0/16", + "--gateway=192.168.0.100", "--gateway=192.170.0.100", + "--ip-range=192.168.1.0/24", + "--aux-address", "a=192.168.1.5", "--aux-address", "b=192.168.1.6", + "--aux-address", "a=192.170.1.5", "--aux-address", "b=192.170.1.6", + "test7") + assertNwIsAvailable(c, "test7") + + // cleanup + for i := 1; i < 8; i++ { + dockerCmd(c, "network", "rm", fmt.Sprintf("test%d", i)) + } +} + +func (s *DockerNetworkSuite) TestDockerNetworkInspect(c *check.C) { + // if unspecified, network gateway will be selected from inside preferred pool + dockerCmd(c, "network", "create", "--driver=bridge", "--subnet=172.28.0.0/16", "--ip-range=172.28.5.0/24", "--gateway=172.28.5.254", "br0") + assertNwIsAvailable(c, "br0") + + nr := getNetworkResource(c, "br0") + c.Assert(nr.Driver, check.Equals, "bridge") + c.Assert(nr.Scope, check.Equals, "local") + c.Assert(nr.IPAM.Driver, check.Equals, "default") + c.Assert(len(nr.IPAM.Config), check.Equals, 1) + c.Assert(nr.IPAM.Config[0].Subnet, check.Equals, "172.28.0.0/16") + c.Assert(nr.IPAM.Config[0].IPRange, check.Equals, "172.28.5.0/24") + c.Assert(nr.IPAM.Config[0].Gateway, check.Equals, "172.28.5.254") + dockerCmd(c, "network", "rm", "br0") +} + +func (s *DockerNetworkSuite) TestDockerNetworkIpamInvalidCombinations(c *check.C) { + // network with ip-range out of subnet range + _, _, err := dockerCmdWithError("network", "create", "--subnet=192.168.0.0/16", "--ip-range=192.170.0.0/16", "test") + c.Assert(err, check.NotNil) + + // network with multiple gateways for a single subnet + _, _, err = dockerCmdWithError("network", "create", "--subnet=192.168.0.0/16", "--gateway=192.168.0.1", "--gateway=192.168.0.2", "test") + c.Assert(err, check.NotNil) + + // Multiple overlaping subnets in the same network must fail + _, _, err = dockerCmdWithError("network", "create", "--subnet=192.168.0.0/16", "--subnet=192.168.1.0/16", "test") + c.Assert(err, check.NotNil) + + // overlapping subnets across networks must fail + // create a valid test0 network + dockerCmd(c, "network", "create", "--subnet=192.168.0.0/16", "test0") + assertNwIsAvailable(c, "test0") + // create an overlapping test1 network + _, _, err = dockerCmdWithError("network", "create", "--subnet=192.168.128.0/17", "test1") + c.Assert(err, check.NotNil) + dockerCmd(c, "network", "rm", "test0") +} diff --git a/vendor/src/github.com/docker/libkv/README.md b/vendor/src/github.com/docker/libkv/README.md index b433e536be..dc4286e2b9 100644 --- a/vendor/src/github.com/docker/libkv/README.md +++ b/vendor/src/github.com/docker/libkv/README.md @@ -68,9 +68,16 @@ func main() { You can find other usage examples for `libkv` under the `docker/swarm` or `docker/libnetwork` repositories. +## Supported versions + +`libkv` supports: +- Consul version >= `0.5.1` because it uses Sessions with `Delete` behavior for the use of `TTLs` (mimics zookeeper's Ephemeral node support), If you don't plan to use `TTLs`: you can use Consul version `0.4.0+`. +- Etcd version >= `2.0` because it uses the `2.0.0` branch of the `coreos/go-etcd` client, this might change in the future as the support for `APIv3` comes along. +- Zookeeper version >= `3.4.5`. Although this might work with previous version but this remains untested as of now. + ## TLS -The etcd backend supports etcd servers that require TLS Client Authentication. Zookeeper and Consul support are planned. This feature is somewhat experimental and the store.ClientTLSConfig struct may change to accommodate the additional backends. +The etcd backend supports etcd servers that require TLS Client Authentication. Zookeeper and Consul support are planned. This feature is somewhat experimental and the store.ClientTLSConfig struct may change to accommodate the additional backends. ## Warning diff --git a/vendor/src/github.com/docker/libkv/store/boltdb/boltdb.go b/vendor/src/github.com/docker/libkv/store/boltdb/boltdb.go index a2f8e1f87d..2446fe335b 100644 --- a/vendor/src/github.com/docker/libkv/store/boltdb/boltdb.go +++ b/vendor/src/github.com/docker/libkv/store/boltdb/boltdb.go @@ -6,7 +6,9 @@ import ( "errors" "os" "path/filepath" + "sync" "sync/atomic" + "time" "github.com/boltdb/bolt" "github.com/docker/libkv" @@ -25,15 +27,29 @@ var ( ErrBoltAPIUnsupported = errors.New("API not supported by BoltDB backend") ) +const ( + filePerm os.FileMode = 0644 +) + //BoltDB type implements the Store interface type BoltDB struct { client *bolt.DB boltBucket []byte dbIndex uint64 + path string + timeout time.Duration + // By default libkv opens and closes the bolt DB connection for every + // get/put operation. This allows multiple apps to use a Bolt DB at the + // same time. + // PersistConnection flag provides an option to override ths behavior. + // ie: open the connection in New and use it till Close is called. + PersistConnection bool + sync.Mutex } const ( libkvmetadatalen = 8 + transientTimeout = time.Duration(10) * time.Second ) // Register registers boltdb to libkv @@ -43,6 +59,12 @@ func Register() { // New opens a new BoltDB connection to the specified path and bucket func New(endpoints []string, options *store.Config) (store.Store, error) { + var ( + db *bolt.DB + err error + boltOptions *bolt.Options + ) + if len(endpoints) > 1 { return nil, ErrMultipleEndpointsUnsupported } @@ -52,39 +74,81 @@ func New(endpoints []string, options *store.Config) (store.Store, error) { } dir, _ := filepath.Split(endpoints[0]) - if err := os.MkdirAll(dir, 0750); err != nil { + if err = os.MkdirAll(dir, 0750); err != nil { return nil, err } - var boltOptions *bolt.Options - if options != nil { + if options.PersistConnection { boltOptions = &bolt.Options{Timeout: options.ConnectionTimeout} - } - db, err := bolt.Open(endpoints[0], 0644, boltOptions) - if err != nil { - return nil, err + db, err = bolt.Open(endpoints[0], filePerm, boltOptions) + if err != nil { + return nil, err + } } - b := &BoltDB{} + b := &BoltDB{ + client: db, + path: endpoints[0], + boltBucket: []byte(options.Bucket), + timeout: transientTimeout, + PersistConnection: options.PersistConnection, + } - b.client = db - b.boltBucket = []byte(options.Bucket) return b, nil } +func (b *BoltDB) reset() { + b.path = "" + b.boltBucket = []byte{} +} + +func (b *BoltDB) getDBhandle() (*bolt.DB, error) { + var ( + db *bolt.DB + err error + ) + if !b.PersistConnection { + boltOptions := &bolt.Options{Timeout: b.timeout} + if db, err = bolt.Open(b.path, filePerm, boltOptions); err != nil { + return nil, err + } + b.client = db + } + + return b.client, nil +} + +func (b *BoltDB) releaseDBhandle() { + if !b.PersistConnection { + b.client.Close() + } +} + // Get the value at "key". BoltDB doesn't provide an inbuilt last modified index with every kv pair. Its implemented by // by a atomic counter maintained by the libkv and appened to the value passed by the client. func (b *BoltDB) Get(key string) (*store.KVPair, error) { - var val []byte + var ( + val []byte + db *bolt.DB + err error + ) + b.Lock() + defer b.Unlock() - db := b.client - err := db.View(func(tx *bolt.Tx) error { + if db, err = b.getDBhandle(); err != nil { + return nil, err + } + defer b.releaseDBhandle() + + err = db.View(func(tx *bolt.Tx) error { bucket := tx.Bucket(b.boltBucket) if bucket == nil { - return (ErrBoltBucketNotFound) + return ErrBoltBucketNotFound } - val = bucket.Get([]byte(key)) + v := bucket.Get([]byte(key)) + val = make([]byte, len(v)) + copy(val, v) return nil }) @@ -104,11 +168,22 @@ func (b *BoltDB) Get(key string) (*store.KVPair, error) { //Put the key, value pair. index number metadata is prepended to the value func (b *BoltDB) Put(key string, value []byte, opts *store.WriteOptions) error { - var dbIndex uint64 - db := b.client + var ( + dbIndex uint64 + db *bolt.DB + err error + ) + b.Lock() + defer b.Unlock() + dbval := make([]byte, libkvmetadatalen) - err := db.Update(func(tx *bolt.Tx) error { + if db, err = b.getDBhandle(); err != nil { + return err + } + defer b.releaseDBhandle() + + err = db.Update(func(tx *bolt.Tx) error { bucket, err := tx.CreateBucketIfNotExists(b.boltBucket) if err != nil { return err @@ -129,12 +204,22 @@ func (b *BoltDB) Put(key string, value []byte, opts *store.WriteOptions) error { //Delete the value for the given key. func (b *BoltDB) Delete(key string) error { - db := b.client + var ( + db *bolt.DB + err error + ) + b.Lock() + defer b.Unlock() - err := db.Update(func(tx *bolt.Tx) error { + if db, err = b.getDBhandle(); err != nil { + return err + } + defer b.releaseDBhandle() + + err = db.Update(func(tx *bolt.Tx) error { bucket := tx.Bucket(b.boltBucket) if bucket == nil { - return (ErrBoltBucketNotFound) + return ErrBoltBucketNotFound } err := bucket.Delete([]byte(key)) return err @@ -144,13 +229,23 @@ func (b *BoltDB) Delete(key string) error { // Exists checks if the key exists inside the store func (b *BoltDB) Exists(key string) (bool, error) { - var val []byte + var ( + val []byte + db *bolt.DB + err error + ) + b.Lock() + defer b.Unlock() - db := b.client - err := db.View(func(tx *bolt.Tx) error { + if db, err = b.getDBhandle(); err != nil { + return false, err + } + defer b.releaseDBhandle() + + err = db.View(func(tx *bolt.Tx) error { bucket := tx.Bucket(b.boltBucket) if bucket == nil { - return (ErrBoltBucketNotFound) + return ErrBoltBucketNotFound } val = bucket.Get([]byte(key)) @@ -166,22 +261,35 @@ func (b *BoltDB) Exists(key string) (bool, error) { // List returns the range of keys starting with the passed in prefix func (b *BoltDB) List(keyPrefix string) ([]*store.KVPair, error) { + var ( + db *bolt.DB + err error + ) + b.Lock() + defer b.Unlock() + kv := []*store.KVPair{} - db := b.client - err := db.View(func(tx *bolt.Tx) error { + if db, err = b.getDBhandle(); err != nil { + return nil, err + } + defer b.releaseDBhandle() + + err = db.View(func(tx *bolt.Tx) error { bucket := tx.Bucket(b.boltBucket) if bucket == nil { - return (ErrBoltBucketNotFound) + return ErrBoltBucketNotFound } cursor := bucket.Cursor() prefix := []byte(keyPrefix) - for key, val := cursor.Seek(prefix); bytes.HasPrefix(key, prefix); key, val = cursor.Next() { + for key, v := cursor.Seek(prefix); bytes.HasPrefix(key, prefix); key, v = cursor.Next() { - dbIndex := binary.LittleEndian.Uint64(val[:libkvmetadatalen]) - val = val[libkvmetadatalen:] + dbIndex := binary.LittleEndian.Uint64(v[:libkvmetadatalen]) + v = v[libkvmetadatalen:] + val := make([]byte, len(v)) + copy(val, v) kv = append(kv, &store.KVPair{ Key: string(key), @@ -201,22 +309,30 @@ func (b *BoltDB) List(keyPrefix string) ([]*store.KVPair, error) { // has not been modified in the meantime, throws an // error if this is the case func (b *BoltDB) AtomicDelete(key string, previous *store.KVPair) (bool, error) { - var val []byte - var dbIndex uint64 + var ( + val []byte + db *bolt.DB + err error + ) + b.Lock() + defer b.Unlock() if previous == nil { return false, store.ErrPreviousNotSpecified } - db := b.client + if db, err = b.getDBhandle(); err != nil { + return false, err + } + defer b.releaseDBhandle() - err := db.Update(func(tx *bolt.Tx) error { + err = db.Update(func(tx *bolt.Tx) error { bucket := tx.Bucket(b.boltBucket) if bucket == nil { return ErrBoltBucketNotFound } val = bucket.Get([]byte(key)) - dbIndex = binary.LittleEndian.Uint64(val[:libkvmetadatalen]) + dbIndex := binary.LittleEndian.Uint64(val[:libkvmetadatalen]) if dbIndex != previous.LastIndex { return store.ErrKeyModified } @@ -232,13 +348,23 @@ func (b *BoltDB) AtomicDelete(key string, previous *store.KVPair) (bool, error) // AtomicPut puts a value at "key" if the key has not been // modified since the last Put, throws an error if this is the case func (b *BoltDB) AtomicPut(key string, value []byte, previous *store.KVPair, options *store.WriteOptions) (bool, *store.KVPair, error) { - var val []byte - var dbIndex uint64 + var ( + val []byte + dbIndex uint64 + db *bolt.DB + err error + ) + b.Lock() + defer b.Unlock() + dbval := make([]byte, libkvmetadatalen) - db := b.client + if db, err = b.getDBhandle(); err != nil { + return false, nil, err + } + defer b.releaseDBhandle() - err := db.Update(func(tx *bolt.Tx) error { + err = db.Update(func(tx *bolt.Tx) error { var err error bucket := tx.Bucket(b.boltBucket) if bucket == nil { @@ -285,18 +411,35 @@ func (b *BoltDB) AtomicPut(key string, value []byte, previous *store.KVPair, opt // Close the db connection to the BoltDB func (b *BoltDB) Close() { - db := b.client + b.Lock() + defer b.Unlock() - db.Close() + if !b.PersistConnection { + b.reset() + } else { + b.client.Close() + } + return } // DeleteTree deletes a range of keys with a given prefix func (b *BoltDB) DeleteTree(keyPrefix string) error { - db := b.client - err := db.Update(func(tx *bolt.Tx) error { + var ( + db *bolt.DB + err error + ) + b.Lock() + defer b.Unlock() + + if db, err = b.getDBhandle(); err != nil { + return err + } + defer b.releaseDBhandle() + + err = db.Update(func(tx *bolt.Tx) error { bucket := tx.Bucket(b.boltBucket) if bucket == nil { - return (ErrBoltBucketNotFound) + return ErrBoltBucketNotFound } cursor := bucket.Cursor() diff --git a/vendor/src/github.com/docker/libkv/store/consul/consul.go b/vendor/src/github.com/docker/libkv/store/consul/consul.go index 76762ce54a..1462d2dae3 100644 --- a/vendor/src/github.com/docker/libkv/store/consul/consul.go +++ b/vendor/src/github.com/docker/libkv/store/consul/consul.go @@ -35,7 +35,8 @@ type Consul struct { } type consulLock struct { - lock *api.Lock + lock *api.Lock + renewCh chan struct{} } // Register registers consul to libkv @@ -87,7 +88,7 @@ func (s *Consul) setTLS(tls *tls.Config) { s.config.Scheme = "https" } -// SetTimeout sets the timout for connecting to Consul +// SetTimeout sets the timeout for connecting to Consul func (s *Consul) setTimeout(time time.Duration) { s.config.WaitTime = time } @@ -360,32 +361,63 @@ func (s *Consul) WatchTree(directory string, stopCh <-chan struct{}) (<-chan []* // NewLock returns a handle to a lock struct which can // be used to provide mutual exclusion on a key func (s *Consul) NewLock(key string, options *store.LockOptions) (store.Locker, error) { - consulOpts := &api.LockOptions{ + lockOpts := &api.LockOptions{ Key: s.normalize(key), } + lock := &consulLock{} + if options != nil { - consulOpts.Value = options.Value + // Set optional TTL on Lock + if options.TTL != 0 { + entry := &api.SessionEntry{ + Behavior: api.SessionBehaviorRelease, // Release the lock when the session expires + TTL: (options.TTL / 2).String(), // Consul multiplies the TTL by 2x + LockDelay: 1 * time.Millisecond, // Virtually disable lock delay + } + + // Create the key session + session, _, err := s.client.Session().Create(entry, nil) + if err != nil { + return nil, err + } + + // Place the session on lock + lockOpts.Session = session + + // Renew the session ttl lock periodically + go s.client.Session().RenewPeriodic(entry.TTL, session, nil, options.RenewLock) + lock.renewCh = options.RenewLock + } + + // Set optional value on Lock + if options.Value != nil { + lockOpts.Value = options.Value + } } - l, err := s.client.LockOpts(consulOpts) + l, err := s.client.LockOpts(lockOpts) if err != nil { return nil, err } - return &consulLock{lock: l}, nil + lock.lock = l + return lock, nil } // Lock attempts to acquire the lock and blocks while // doing so. It returns a channel that is closed if our // lock is lost or if an error occurs -func (l *consulLock) Lock() (<-chan struct{}, error) { - return l.lock.Lock(nil) +func (l *consulLock) Lock(stopChan chan struct{}) (<-chan struct{}, error) { + return l.lock.Lock(stopChan) } // Unlock the "key". Calling unlock while // not holding the lock will throw an error func (l *consulLock) Unlock() error { + if l.renewCh != nil { + close(l.renewCh) + } return l.lock.Unlock() } diff --git a/vendor/src/github.com/docker/libkv/store/etcd/etcd.go b/vendor/src/github.com/docker/libkv/store/etcd/etcd.go index 233b3c671e..d27b13a758 100644 --- a/vendor/src/github.com/docker/libkv/store/etcd/etcd.go +++ b/vendor/src/github.com/docker/libkv/store/etcd/etcd.go @@ -2,6 +2,7 @@ package etcd import ( "crypto/tls" + "errors" "net" "net/http" "strings" @@ -12,6 +13,13 @@ import ( "github.com/docker/libkv/store" ) +var ( + // ErrAbortTryLock is thrown when a user stops trying to seek the lock + // by sending a signal to the stop chan, this is used to verify if the + // operation succeeded + ErrAbortTryLock = errors.New("lock operation aborted") +) + // Etcd is the receiver type for the // Store interface type Etcd struct { @@ -19,12 +27,13 @@ type Etcd struct { } type etcdLock struct { - client *etcd.Client - stopLock chan struct{} - key string - value string - last *etcd.Response - ttl uint64 + client *etcd.Client + stopLock chan struct{} + stopRenew chan struct{} + key string + value string + last *etcd.Response + ttl uint64 } const ( @@ -395,6 +404,7 @@ func (s *Etcd) DeleteTree(directory string) error { func (s *Etcd) NewLock(key string, options *store.LockOptions) (lock store.Locker, err error) { var value string ttl := uint64(time.Duration(defaultLockTTL).Seconds()) + renewCh := make(chan struct{}) // Apply options on Lock if options != nil { @@ -404,14 +414,18 @@ func (s *Etcd) NewLock(key string, options *store.LockOptions) (lock store.Locke if options.TTL != 0 { ttl = uint64(options.TTL.Seconds()) } + if options.RenewLock != nil { + renewCh = options.RenewLock + } } // Create lock object lock = &etcdLock{ - client: s.client, - key: key, - value: value, - ttl: ttl, + client: s.client, + stopRenew: renewCh, + key: key, + value: value, + ttl: ttl, } return lock, nil @@ -420,13 +434,13 @@ func (s *Etcd) NewLock(key string, options *store.LockOptions) (lock store.Locke // Lock attempts to acquire the lock and blocks while // doing so. It returns a channel that is closed if our // lock is lost or if an error occurs -func (l *etcdLock) Lock() (<-chan struct{}, error) { +func (l *etcdLock) Lock(stopChan chan struct{}) (<-chan struct{}, error) { key := store.Normalize(l.key) - // Lock holder channels + // Lock holder channel lockHeld := make(chan struct{}) - stopLocking := make(chan struct{}) + stopLocking := l.stopRenew var lastIndex uint64 @@ -454,7 +468,18 @@ func (l *etcdLock) Lock() (<-chan struct{}, error) { // Seeker section chW := make(chan *etcd.Response) chWStop := make(chan bool) - l.waitLock(key, chW, chWStop) + free := make(chan bool) + + go l.waitLock(key, chW, chWStop, free) + + // Wait for the key to be available or for + // a signal to stop trying to lock the key + select { + case _ = <-free: + break + case _ = <-stopChan: + return nil, ErrAbortTryLock + } // Delete or Expire event occured // Retry @@ -467,10 +492,10 @@ func (l *etcdLock) Lock() (<-chan struct{}, error) { // Hold the lock as long as we can // Updates the key ttl periodically until we receive // an explicit stop signal from the Unlock method -func (l *etcdLock) holdLock(key string, lockHeld chan struct{}, stopLocking chan struct{}) { +func (l *etcdLock) holdLock(key string, lockHeld chan struct{}, stopLocking <-chan struct{}) { defer close(lockHeld) - update := time.NewTicker(defaultUpdateTime) + update := time.NewTicker(time.Duration(l.ttl) * time.Second / 3) defer update.Stop() var err error @@ -490,11 +515,12 @@ func (l *etcdLock) holdLock(key string, lockHeld chan struct{}, stopLocking chan } // WaitLock simply waits for the key to be available for creation -func (l *etcdLock) waitLock(key string, eventCh chan *etcd.Response, stopWatchCh chan bool) { +func (l *etcdLock) waitLock(key string, eventCh chan *etcd.Response, stopWatchCh chan bool, free chan<- bool) { go l.client.Watch(key, 0, false, eventCh, stopWatchCh) + for event := range eventCh { if event.Action == "delete" || event.Action == "expire" { - return + free <- true } } } diff --git a/vendor/src/github.com/docker/libkv/store/store.go b/vendor/src/github.com/docker/libkv/store/store.go index 108242bd5c..e7aace9b3d 100644 --- a/vendor/src/github.com/docker/libkv/store/store.go +++ b/vendor/src/github.com/docker/libkv/store/store.go @@ -43,6 +43,7 @@ type Config struct { TLS *tls.Config ConnectionTimeout time.Duration Bucket string + PersistConnection bool } // ClientTLSConfig contains data for a Client TLS configuration in the form @@ -113,13 +114,14 @@ type WriteOptions struct { // LockOptions contains optional request parameters type LockOptions struct { - Value []byte // Optional, value to associate with the lock - TTL time.Duration // Optional, expiration ttl associated with the lock + Value []byte // Optional, value to associate with the lock + TTL time.Duration // Optional, expiration ttl associated with the lock + RenewLock chan struct{} // Optional, chan used to control and stop the session ttl renewal for the lock } // Locker provides locking mechanism on top of the store. // Similar to `sync.Lock` except it may return errors. type Locker interface { - Lock() (<-chan struct{}, error) + Lock(stopChan chan struct{}) (<-chan struct{}, error) Unlock() error } diff --git a/vendor/src/github.com/docker/libkv/store/zookeeper/zookeeper.go b/vendor/src/github.com/docker/libkv/store/zookeeper/zookeeper.go index ff6b481947..9e999664e9 100644 --- a/vendor/src/github.com/docker/libkv/store/zookeeper/zookeeper.go +++ b/vendor/src/github.com/docker/libkv/store/zookeeper/zookeeper.go @@ -10,6 +10,9 @@ import ( ) const ( + // SOH control character + SOH = "\x01" + defaultTimeout = 10 * time.Second ) @@ -72,6 +75,12 @@ func (s *Zookeeper) Get(key string) (pair *store.KVPair, err error) { return nil, err } + // FIXME handle very rare cases where Get returns the + // SOH control character instead of the actual value + if string(resp) == SOH { + return s.Get(store.Normalize(key)) + } + pair = &store.KVPair{ Key: key, Value: resp, @@ -301,7 +310,7 @@ func (s *Zookeeper) AtomicPut(key string, value []byte, previous *store.KVPair, // Zookeeper will complain if the directory doesn't exist. if err == zk.ErrNoNode { // Create the directory - parts := store.SplitKey(key) + parts := store.SplitKey(strings.TrimSuffix(key, "/")) parts = parts[:len(parts)-1] if err = s.createFullPath(parts, false); err != nil { // Failed to create the directory. @@ -371,7 +380,7 @@ func (s *Zookeeper) NewLock(key string, options *store.LockOptions) (lock store. // Lock attempts to acquire the lock and blocks while // doing so. It returns a channel that is closed if our // lock is lost or if an error occurs -func (l *zookeeperLock) Lock() (<-chan struct{}, error) { +func (l *zookeeperLock) Lock(stopChan chan struct{}) (<-chan struct{}, error) { err := l.lock.Lock() if err == nil { diff --git a/vendor/src/github.com/docker/libnetwork/.gitignore b/vendor/src/github.com/docker/libnetwork/.gitignore index 0529bde798..0e0d489204 100644 --- a/vendor/src/github.com/docker/libnetwork/.gitignore +++ b/vendor/src/github.com/docker/libnetwork/.gitignore @@ -29,7 +29,8 @@ cmd/dnet/dnet *.tmp *.coverprofile -# IDE files +# IDE files and folders .project +.settings/ libnetwork-build.created diff --git a/vendor/src/github.com/docker/libnetwork/bitseq/sequence.go b/vendor/src/github.com/docker/libnetwork/bitseq/sequence.go index 047f4dbaa7..14304e3400 100644 --- a/vendor/src/github.com/docker/libnetwork/bitseq/sequence.go +++ b/vendor/src/github.com/docker/libnetwork/bitseq/sequence.go @@ -5,6 +5,7 @@ package bitseq import ( "encoding/binary" + "encoding/json" "fmt" "sync" @@ -16,10 +17,10 @@ import ( // If needed we can think of making these configurable const ( blockLen = uint32(32) - blockBytes = blockLen / 8 + blockBytes = uint64(blockLen / 8) blockMAX = uint32(1<= h.bits { return invalidPos, fmt.Errorf("invalid bit range [%d, %d]", start, end) } @@ -198,7 +203,7 @@ func (h *Handle) SetAnyInRange(start, end uint32) (uint32, error) { } // SetAny atomically sets the first unset bit in the sequence and returns the corresponding ordinal -func (h *Handle) SetAny() (uint32, error) { +func (h *Handle) SetAny() (uint64, error) { if h.Unselected() == 0 { return invalidPos, errNoBitAvailable } @@ -206,7 +211,7 @@ func (h *Handle) SetAny() (uint32, error) { } // Set atomically sets the corresponding bit in the sequence -func (h *Handle) Set(ordinal uint32) error { +func (h *Handle) Set(ordinal uint64) error { if err := h.validateOrdinal(ordinal); err != nil { return err } @@ -215,7 +220,7 @@ func (h *Handle) Set(ordinal uint32) error { } // Unset atomically unsets the corresponding bit in the sequence -func (h *Handle) Unset(ordinal uint32) error { +func (h *Handle) Unset(ordinal uint64) error { if err := h.validateOrdinal(ordinal); err != nil { return err } @@ -225,7 +230,7 @@ func (h *Handle) Unset(ordinal uint32) error { // IsSet atomically checks if the ordinal bit is set. In case ordinal // is outside of the bit sequence limits, false is returned. -func (h *Handle) IsSet(ordinal uint32) bool { +func (h *Handle) IsSet(ordinal uint64) bool { if err := h.validateOrdinal(ordinal); err != nil { return false } @@ -236,15 +241,21 @@ func (h *Handle) IsSet(ordinal uint32) bool { } // set/reset the bit -func (h *Handle) set(ordinal, start, end uint32, any bool, release bool) (uint32, error) { +func (h *Handle) set(ordinal, start, end uint64, any bool, release bool) (uint64, error) { var ( - bitPos uint32 - bytePos uint32 - ret uint32 + bitPos uint64 + bytePos uint64 + ret uint64 err error ) for { + if h.store != nil { + if err := h.store.GetObject(datastore.Key(h.Key()...), h); err != nil && err != datastore.ErrKeyNotFound { + return ret, err + } + } + h.Lock() // Get position if available if release { @@ -298,7 +309,7 @@ func (h *Handle) set(ordinal, start, end uint32, any bool, release bool) (uint32 } // checks is needed because to cover the case where the number of bits is not a multiple of blockLen -func (h *Handle) validateOrdinal(ordinal uint32) error { +func (h *Handle) validateOrdinal(ordinal uint64) error { if ordinal >= h.bits { return fmt.Errorf("bit does not belong to the sequence") } @@ -306,8 +317,23 @@ func (h *Handle) validateOrdinal(ordinal uint32) error { } // Destroy removes from the datastore the data belonging to this handle -func (h *Handle) Destroy() { - h.deleteFromStore() +func (h *Handle) Destroy() error { + for { + if err := h.deleteFromStore(); err != nil { + if _, ok := err.(types.RetryError); !ok { + return fmt.Errorf("internal failure while destroying the sequence: %v", err) + } + // Fetch latest + if err := h.store.GetObject(datastore.Key(h.Key()...), h); err != nil { + if err == datastore.ErrKeyNotFound { // already removed + return nil + } + return fmt.Errorf("failed to fetch from store when destroying the sequence: %v", err) + } + continue + } + return nil + } } // ToByteArray converts this handle's data into a byte array @@ -315,9 +341,9 @@ func (h *Handle) ToByteArray() ([]byte, error) { h.Lock() defer h.Unlock() - ba := make([]byte, 8) - binary.BigEndian.PutUint32(ba[0:], h.bits) - binary.BigEndian.PutUint32(ba[4:], h.unselected) + ba := make([]byte, 16) + binary.BigEndian.PutUint64(ba[0:], h.bits) + binary.BigEndian.PutUint64(ba[8:], h.unselected) bm, err := h.head.toByteArray() if err != nil { return nil, fmt.Errorf("failed to serialize head: %s", err.Error()) @@ -334,27 +360,27 @@ func (h *Handle) FromByteArray(ba []byte) error { } nh := &sequence{} - err := nh.fromByteArray(ba[8:]) + err := nh.fromByteArray(ba[16:]) if err != nil { return fmt.Errorf("failed to deserialize head: %s", err.Error()) } h.Lock() h.head = nh - h.bits = binary.BigEndian.Uint32(ba[0:4]) - h.unselected = binary.BigEndian.Uint32(ba[4:8]) + h.bits = binary.BigEndian.Uint64(ba[0:8]) + h.unselected = binary.BigEndian.Uint64(ba[8:16]) h.Unlock() return nil } // Bits returns the length of the bit sequence -func (h *Handle) Bits() uint32 { +func (h *Handle) Bits() uint64 { return h.bits } // Unselected returns the number of bits which are not selected -func (h *Handle) Unselected() uint32 { +func (h *Handle) Unselected() uint64 { h.Lock() defer h.Unlock() return h.unselected @@ -367,8 +393,40 @@ func (h *Handle) String() string { h.app, h.id, h.dbIndex, h.bits, h.unselected, h.head.toString()) } +// MarshalJSON encodes Handle into json message +func (h *Handle) MarshalJSON() ([]byte, error) { + m := map[string]interface{}{ + "id": h.id, + } + + b, err := h.ToByteArray() + if err != nil { + return nil, err + } + m["sequence"] = b + return json.Marshal(m) +} + +// UnmarshalJSON decodes json message into Handle +func (h *Handle) UnmarshalJSON(data []byte) error { + var ( + m map[string]interface{} + b []byte + err error + ) + if err = json.Unmarshal(data, &m); err != nil { + return err + } + h.id = m["id"].(string) + bi, _ := json.Marshal(m["sequence"]) + if err := json.Unmarshal(bi, &b); err != nil { + return err + } + return h.FromByteArray(b) +} + // getFirstAvailable looks for the first unset bit in passed mask starting from start -func getFirstAvailable(head *sequence, start uint32) (uint32, uint32, error) { +func getFirstAvailable(head *sequence, start uint64) (uint64, uint64, error) { // Find sequence which contains the start bit byteStart, bitStart := ordinalToPos(start) current, _, _, inBlockBytePos := findSequence(head, byteStart) @@ -392,7 +450,7 @@ func getFirstAvailable(head *sequence, start uint32) (uint32, uint32, error) { // checkIfAvailable checks if the bit correspondent to the specified ordinal is unset // If the ordinal is beyond the sequence limits, a negative response is returned -func checkIfAvailable(head *sequence, ordinal uint32) (uint32, uint32, error) { +func checkIfAvailable(head *sequence, ordinal uint64) (uint64, uint64, error) { bytePos, bitPos := ordinalToPos(ordinal) // Find the sequence containing this byte @@ -412,7 +470,7 @@ func checkIfAvailable(head *sequence, ordinal uint32) (uint32, uint32, error) { // sequence containing the byte (current), the pointer to the previous sequence, // the number of blocks preceding the block containing the byte inside the current sequence. // If bytePos is outside of the list, function will return (nil, nil, 0, invalidPos) -func findSequence(head *sequence, bytePos uint32) (*sequence, *sequence, uint32, uint32) { +func findSequence(head *sequence, bytePos uint64) (*sequence, *sequence, uint64, uint64) { // Find the sequence containing this byte previous := head current := head @@ -453,7 +511,7 @@ func findSequence(head *sequence, bytePos uint32) (*sequence, *sequence, uint32, // A) block is first in current: [prev seq] [new] [modified current seq] [next seq] // B) block is last in current: [prev seq] [modified current seq] [new] [next seq] // C) block is in the middle of current: [prev seq] [curr pre] [new] [curr post] [next seq] -func pushReservation(bytePos, bitPos uint32, head *sequence, release bool) *sequence { +func pushReservation(bytePos, bitPos uint64, head *sequence, release bool) *sequence { // Store list's head newHead := head @@ -541,18 +599,18 @@ func mergeSequences(seq *sequence) { } } -func getNumBlocks(numBits uint32) uint32 { - numBlocks := numBits / blockLen - if numBits%blockLen != 0 { +func getNumBlocks(numBits uint64) uint64 { + numBlocks := numBits / uint64(blockLen) + if numBits%uint64(blockLen) != 0 { numBlocks++ } return numBlocks } -func ordinalToPos(ordinal uint32) (uint32, uint32) { +func ordinalToPos(ordinal uint64) (uint64, uint64) { return ordinal / 8, ordinal % 8 } -func posToOrdinal(bytePos, bitPos uint32) uint32 { +func posToOrdinal(bytePos, bitPos uint64) uint64 { return bytePos*8 + bitPos } diff --git a/vendor/src/github.com/docker/libnetwork/bitseq/store.go b/vendor/src/github.com/docker/libnetwork/bitseq/store.go index 8012a413d2..df50331227 100644 --- a/vendor/src/github.com/docker/libnetwork/bitseq/store.go +++ b/vendor/src/github.com/docker/libnetwork/bitseq/store.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" - log "github.com/Sirupsen/logrus" "github.com/docker/libnetwork/datastore" "github.com/docker/libnetwork/types" ) @@ -25,27 +24,16 @@ func (h *Handle) KeyPrefix() []string { // Value marshals the data to be stored in the KV store func (h *Handle) Value() []byte { - b, err := h.ToByteArray() + b, err := json.Marshal(h) if err != nil { - log.Warnf("Failed to serialize Handle: %v", err) - b = []byte{} + return nil } - jv, err := json.Marshal(b) - if err != nil { - log.Warnf("Failed to json encode bitseq handler byte array: %v", err) - return []byte{} - } - return jv + return b } // SetValue unmarshals the data from the KV store func (h *Handle) SetValue(value []byte) error { - var b []byte - if err := json.Unmarshal(value, &b); err != nil { - return err - } - - return h.FromByteArray(b) + return json.Unmarshal(value, h) } // Index returns the latest DB Index as seen by this object @@ -70,46 +58,46 @@ func (h *Handle) Exists() bool { return h.dbExists } +// New method returns a handle based on the receiver handle +func (h *Handle) New() datastore.KVObject { + h.Lock() + defer h.Unlock() + + return &Handle{ + app: h.app, + store: h.store, + } +} + +// CopyTo deep copies the handle into the passed destination object +func (h *Handle) CopyTo(o datastore.KVObject) error { + h.Lock() + defer h.Unlock() + + dstH := o.(*Handle) + dstH.bits = h.bits + dstH.unselected = h.unselected + dstH.head = h.head.getCopy() + dstH.app = h.app + dstH.id = h.id + dstH.dbIndex = h.dbIndex + dstH.dbExists = h.dbExists + dstH.store = h.store + + return nil +} + // Skip provides a way for a KV Object to avoid persisting it in the KV Store func (h *Handle) Skip() bool { return false } // DataScope method returns the storage scope of the datastore -func (h *Handle) DataScope() datastore.DataScope { - return datastore.GlobalScope -} - -func (h *Handle) watchForChanges() error { +func (h *Handle) DataScope() string { h.Lock() - store := h.store - h.Unlock() + defer h.Unlock() - if store == nil { - return nil - } - - kvpChan, err := store.KVStore().Watch(datastore.Key(h.Key()...), nil) - if err != nil { - return err - } - go func() { - for { - select { - case kvPair := <-kvpChan: - // Only process remote update - if kvPair != nil && (kvPair.LastIndex != h.Index()) { - err := h.fromDsValue(kvPair.Value) - if err != nil { - log.Warnf("Failed to reconstruct bitseq handle from ds watch: %s", err.Error()) - } else { - h.SetIndex(kvPair.LastIndex) - } - } - } - } - }() - return nil + return h.store.Scope() } func (h *Handle) fromDsValue(value []byte) error { diff --git a/vendor/src/github.com/docker/libnetwork/config/config.go b/vendor/src/github.com/docker/libnetwork/config/config.go index 96c8dab677..80d2fc3af7 100644 --- a/vendor/src/github.com/docker/libnetwork/config/config.go +++ b/vendor/src/github.com/docker/libnetwork/config/config.go @@ -6,20 +6,23 @@ import ( "github.com/BurntSushi/toml" log "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/discovery" + "github.com/docker/docker/pkg/tlsconfig" "github.com/docker/libkv/store" + "github.com/docker/libnetwork/datastore" "github.com/docker/libnetwork/netlabel" ) // Config encapsulates configurations of various Libnetwork components type Config struct { - Daemon DaemonCfg - Cluster ClusterCfg - GlobalStore, LocalStore DatastoreCfg + Daemon DaemonCfg + Cluster ClusterCfg + Scopes map[string]*datastore.ScopeCfg } // DaemonCfg represents libnetwork core configuration type DaemonCfg struct { Debug bool + DataDir string DefaultNetwork string DefaultDriver string Labels []string @@ -34,26 +37,28 @@ type ClusterCfg struct { Heartbeat uint64 } -// DatastoreCfg represents Datastore configuration. -type DatastoreCfg struct { - Embedded bool - Client DatastoreClientCfg -} - -// DatastoreClientCfg represents Datastore Client-only mode configuration -type DatastoreClientCfg struct { - Provider string - Address string - Config *store.Config +// LoadDefaultScopes loads default scope configs for scopes which +// doesn't have explicit user specified configs. +func (c *Config) LoadDefaultScopes(dataDir string) { + for k, v := range datastore.DefaultScopes(dataDir) { + if _, ok := c.Scopes[k]; !ok { + c.Scopes[k] = v + } + } } // ParseConfig parses the libnetwork configuration file func ParseConfig(tomlCfgFile string) (*Config, error) { - var cfg Config - if _, err := toml.DecodeFile(tomlCfgFile, &cfg); err != nil { + cfg := &Config{ + Scopes: map[string]*datastore.ScopeCfg{}, + } + + if _, err := toml.DecodeFile(tomlCfgFile, cfg); err != nil { return nil, err } - return &cfg, nil + + cfg.LoadDefaultScopes(cfg.Daemon.DataDir) + return cfg, nil } // Option is a option setter function type used to pass varios configurations @@ -63,7 +68,7 @@ type Option func(c *Config) // OptionDefaultNetwork function returns an option setter for a default network func OptionDefaultNetwork(dn string) Option { return func(c *Config) { - log.Infof("Option DefaultNetwork: %s", dn) + log.Debugf("Option DefaultNetwork: %s", dn) c.Daemon.DefaultNetwork = strings.TrimSpace(dn) } } @@ -71,7 +76,7 @@ func OptionDefaultNetwork(dn string) Option { // OptionDefaultDriver function returns an option setter for default driver func OptionDefaultDriver(dd string) Option { return func(c *Config) { - log.Infof("Option DefaultDriver: %s", dd) + log.Debugf("Option DefaultDriver: %s", dd) c.Daemon.DefaultDriver = strings.TrimSpace(dd) } } @@ -97,16 +102,56 @@ func OptionLabels(labels []string) Option { // OptionKVProvider function returns an option setter for kvstore provider func OptionKVProvider(provider string) Option { return func(c *Config) { - log.Infof("Option OptionKVProvider: %s", provider) - c.GlobalStore.Client.Provider = strings.TrimSpace(provider) + log.Debugf("Option OptionKVProvider: %s", provider) + if _, ok := c.Scopes[datastore.GlobalScope]; !ok { + c.Scopes[datastore.GlobalScope] = &datastore.ScopeCfg{} + } + c.Scopes[datastore.GlobalScope].Client.Provider = strings.TrimSpace(provider) } } // OptionKVProviderURL function returns an option setter for kvstore url func OptionKVProviderURL(url string) Option { return func(c *Config) { - log.Infof("Option OptionKVProviderURL: %s", url) - c.GlobalStore.Client.Address = strings.TrimSpace(url) + log.Debugf("Option OptionKVProviderURL: %s", url) + if _, ok := c.Scopes[datastore.GlobalScope]; !ok { + c.Scopes[datastore.GlobalScope] = &datastore.ScopeCfg{} + } + c.Scopes[datastore.GlobalScope].Client.Address = strings.TrimSpace(url) + } +} + +// OptionKVOpts function returns an option setter for kvstore options +func OptionKVOpts(opts map[string]string) Option { + return func(c *Config) { + if opts["kv.cacertfile"] != "" && opts["kv.certfile"] != "" && opts["kv.keyfile"] != "" { + log.Info("Option Initializing KV with TLS") + tlsConfig, err := tlsconfig.Client(tlsconfig.Options{ + CAFile: opts["kv.cacertfile"], + CertFile: opts["kv.certfile"], + KeyFile: opts["kv.keyfile"], + }) + if err != nil { + log.Errorf("Unable to set up TLS: %s", err) + return + } + if _, ok := c.Scopes[datastore.GlobalScope]; !ok { + c.Scopes[datastore.GlobalScope] = &datastore.ScopeCfg{} + } + if c.Scopes[datastore.GlobalScope].Client.Config == nil { + c.Scopes[datastore.GlobalScope].Client.Config = &store.Config{TLS: tlsConfig} + } else { + c.Scopes[datastore.GlobalScope].Client.Config.TLS = tlsConfig + } + // Workaround libkv/etcd bug for https + c.Scopes[datastore.GlobalScope].Client.Config.ClientTLS = &store.ClientTLSConfig{ + CACertFile: opts["kv.cacertfile"], + CertFile: opts["kv.certfile"], + KeyFile: opts["kv.keyfile"], + } + } else { + log.Info("Option Initializing KV without TLS") + } } } @@ -124,6 +169,13 @@ func OptionDiscoveryAddress(address string) Option { } } +// OptionDataDir function returns an option setter for data folder +func OptionDataDir(dataDir string) Option { + return func(c *Config) { + c.Daemon.DataDir = dataDir + } +} + // ProcessOptions processes options and stores it in config func (c *Config) ProcessOptions(options ...Option) { for _, opt := range options { @@ -135,7 +187,7 @@ func (c *Config) ProcessOptions(options ...Option) { // IsValidName validates configuration objects supported by libnetwork func IsValidName(name string) bool { - if strings.TrimSpace(name) == "" || strings.Contains(name, ".") { + if strings.TrimSpace(name) == "" { return false } return true @@ -144,23 +196,32 @@ func IsValidName(name string) bool { // OptionLocalKVProvider function returns an option setter for kvstore provider func OptionLocalKVProvider(provider string) Option { return func(c *Config) { - log.Infof("Option OptionLocalKVProvider: %s", provider) - c.LocalStore.Client.Provider = strings.TrimSpace(provider) + log.Debugf("Option OptionLocalKVProvider: %s", provider) + if _, ok := c.Scopes[datastore.LocalScope]; !ok { + c.Scopes[datastore.LocalScope] = &datastore.ScopeCfg{} + } + c.Scopes[datastore.LocalScope].Client.Provider = strings.TrimSpace(provider) } } // OptionLocalKVProviderURL function returns an option setter for kvstore url func OptionLocalKVProviderURL(url string) Option { return func(c *Config) { - log.Infof("Option OptionLocalKVProviderURL: %s", url) - c.LocalStore.Client.Address = strings.TrimSpace(url) + log.Debugf("Option OptionLocalKVProviderURL: %s", url) + if _, ok := c.Scopes[datastore.LocalScope]; !ok { + c.Scopes[datastore.LocalScope] = &datastore.ScopeCfg{} + } + c.Scopes[datastore.LocalScope].Client.Address = strings.TrimSpace(url) } } // OptionLocalKVProviderConfig function returns an option setter for kvstore config func OptionLocalKVProviderConfig(config *store.Config) Option { return func(c *Config) { - log.Infof("Option OptionLocalKVProviderConfig: %v", config) - c.LocalStore.Client.Config = config + log.Debugf("Option OptionLocalKVProviderConfig: %v", config) + if _, ok := c.Scopes[datastore.LocalScope]; !ok { + c.Scopes[datastore.LocalScope] = &datastore.ScopeCfg{} + } + c.Scopes[datastore.LocalScope].Client.Config = config } } diff --git a/vendor/src/github.com/docker/libnetwork/controller.go b/vendor/src/github.com/docker/libnetwork/controller.go index 9a364c5413..e802825010 100644 --- a/vendor/src/github.com/docker/libnetwork/controller.go +++ b/vendor/src/github.com/docker/libnetwork/controller.go @@ -58,6 +58,8 @@ import ( "github.com/docker/libnetwork/datastore" "github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/hostdiscovery" + "github.com/docker/libnetwork/ipamapi" + "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/osl" "github.com/docker/libnetwork/types" ) @@ -116,60 +118,82 @@ type driverData struct { capability driverapi.Capability } +type ipamData struct { + driver ipamapi.Ipam + // default address spaces are provided by ipam driver at registration time + defaultLocalAddressSpace, defaultGlobalAddressSpace string +} + type driverTable map[string]*driverData -type networkTable map[string]*network -type endpointTable map[string]*endpoint + +//type networkTable map[string]*network +//type endpointTable map[string]*endpoint +type ipamTable map[string]*ipamData type sandboxTable map[string]*sandbox type controller struct { - id string - networks networkTable - drivers driverTable - sandboxes sandboxTable - cfg *config.Config - globalStore, localStore datastore.DataStore - discovery hostdiscovery.HostDiscovery - extKeyListener net.Listener + id string + //networks networkTable + drivers driverTable + ipamDrivers ipamTable + sandboxes sandboxTable + cfg *config.Config + stores []datastore.DataStore + discovery hostdiscovery.HostDiscovery + extKeyListener net.Listener + watchCh chan *endpoint + unWatchCh chan *endpoint + svcDb map[string]svcMap sync.Mutex } // New creates a new instance of network controller. func New(cfgOptions ...config.Option) (NetworkController, error) { var cfg *config.Config + cfg = &config.Config{ + Daemon: config.DaemonCfg{ + DriverCfg: make(map[string]interface{}), + }, + Scopes: make(map[string]*datastore.ScopeCfg), + } + if len(cfgOptions) > 0 { - cfg = &config.Config{ - Daemon: config.DaemonCfg{ - DriverCfg: make(map[string]interface{}), - }, - } cfg.ProcessOptions(cfgOptions...) } + cfg.LoadDefaultScopes(cfg.Daemon.DataDir) + c := &controller{ - id: stringid.GenerateRandomID(), - cfg: cfg, - networks: networkTable{}, - sandboxes: sandboxTable{}, - drivers: driverTable{}} - if err := initDrivers(c); err != nil { + id: stringid.GenerateRandomID(), + cfg: cfg, + sandboxes: sandboxTable{}, + drivers: driverTable{}, + ipamDrivers: ipamTable{}, + svcDb: make(map[string]svcMap), + } + + if err := c.initStores(); err != nil { return nil, err } - if cfg != nil { - if err := c.initGlobalStore(); err != nil { - // Failing to initalize datastore is a bad situation to be in. - // But it cannot fail creating the Controller - log.Debugf("Failed to Initialize Datastore due to %v. Operating in non-clustered mode", err) - } + if cfg != nil && cfg.Cluster.Watcher != nil { if err := c.initDiscovery(cfg.Cluster.Watcher); err != nil { // Failing to initalize discovery is a bad situation to be in. // But it cannot fail creating the Controller log.Debugf("Failed to Initialize Discovery : %v", err) } - if err := c.initLocalStore(); err != nil { - log.Debugf("Failed to Initialize LocalDatastore due to %v.", err) - } } + if err := initDrivers(c); err != nil { + return nil, err + } + + if err := initIpams(c, c.getStore(datastore.LocalScope), + c.getStore(datastore.GlobalScope)); err != nil { + return nil, err + } + + c.sandboxCleanup() + if err := c.startExternalKeyListener(); err != nil { return nil, err } @@ -272,43 +296,91 @@ func (c *controller) RegisterDriver(networkType string, driver driverapi.Driver, return nil } +func (c *controller) RegisterIpamDriver(name string, driver ipamapi.Ipam) error { + if !config.IsValidName(name) { + return ErrInvalidName(name) + } + + c.Lock() + _, ok := c.ipamDrivers[name] + c.Unlock() + if ok { + return driverapi.ErrActiveRegistration(name) + } + locAS, glbAS, err := driver.GetDefaultAddressSpaces() + if err != nil { + return fmt.Errorf("ipam driver %s failed to return default address spaces: %v", name, err) + } + c.Lock() + c.ipamDrivers[name] = &ipamData{driver: driver, defaultLocalAddressSpace: locAS, defaultGlobalAddressSpace: glbAS} + c.Unlock() + + log.Debugf("Registering ipam provider: %s", name) + + return nil +} + // NewNetwork creates a new network of the specified network type. The options // are network specific and modeled in a generic way. func (c *controller) NewNetwork(networkType, name string, options ...NetworkOption) (Network, error) { if !config.IsValidName(name) { return nil, ErrInvalidName(name) } - // Check if a network already exists with the specified network name - c.Lock() - for _, n := range c.networks { - if n.name == name { - c.Unlock() - return nil, NetworkNameError(name) - } - } - c.Unlock() // Construct the network object network := &network{ name: name, networkType: networkType, + generic: map[string]interface{}{netlabel.GenericData: make(map[string]string)}, + ipamType: ipamapi.DefaultIPAM, id: stringid.GenerateRandomID(), ctrlr: c, - endpoints: endpointTable{}, persist: true, + drvOnce: &sync.Once{}, } network.processOptions(options...) - if err := c.addNetwork(network); err != nil { + // Make sure we have a driver available for this network type + // before we allocate anything. + if _, err := network.driver(); err != nil { return nil, err } - if err := c.updateToStore(network); err != nil { - log.Warnf("couldnt create network %s: %v", network.name, err) - if e := network.Delete(); e != nil { - log.Warnf("couldnt cleanup network %s: %v", network.name, err) + err := network.ipamAllocate() + if err != nil { + return nil, err + } + defer func() { + if err != nil { + network.ipamRelease() } + }() + + if err := c.addNetwork(network); err != nil { + return nil, err + } + defer func() { + if err != nil { + if e := network.deleteNetwork(); e != nil { + log.Warnf("couldn't roll back driver network on network %s creation failure: %v", network.name, err) + } + } + }() + + if err = c.updateToStore(network); err != nil { + return nil, err + } + defer func() { + if err != nil { + if e := c.deleteFromStore(network); e != nil { + log.Warnf("couldnt rollback from store, network %s on failure (%v): %v", network.name, err, e) + } + } + }() + + network.epCnt = &endpointCnt{n: network} + if err = c.updateToStore(network.epCnt); err != nil { return nil, err } @@ -316,49 +388,28 @@ func (c *controller) NewNetwork(networkType, name string, options ...NetworkOpti } func (c *controller) addNetwork(n *network) error { - - c.Lock() - // Check if a driver for the specified network type is available - dd, ok := c.drivers[n.networkType] - c.Unlock() - - if !ok { - var err error - dd, err = c.loadDriver(n.networkType) - if err != nil { - return err - } - } - - n.Lock() - n.svcRecords = svcMap{} - n.driver = dd.driver - n.dataScope = dd.capability.DataScope - d := n.driver - n.Unlock() - - // Create the network - if err := d.CreateNetwork(n.id, n.generic); err != nil { + d, err := n.driver() + if err != nil { return err } - if n.isGlobalScoped() { - if err := n.watchEndpoints(); err != nil { - return err - } + + // Create the network + if err := d.CreateNetwork(n.id, n.generic, n.getIPData(4), n.getIPData(6)); err != nil { + return err } - c.Lock() - c.networks[n.id] = n - c.Unlock() return nil } func (c *controller) Networks() []Network { - c.Lock() - defer c.Unlock() + var list []Network - list := make([]Network, 0, len(c.networks)) - for _, n := range c.networks { + networks, err := c.getNetworksFromStore() + if err != nil { + log.Error(err) + } + + for _, n := range networks { list = append(list, n) } @@ -400,12 +451,13 @@ func (c *controller) NetworkByID(id string) (Network, error) { if id == "" { return nil, ErrInvalidID(id) } - c.Lock() - defer c.Unlock() - if n, ok := c.networks[id]; ok { - return n, nil + + n, err := c.getNetworkFromStore(id) + if err != nil { + return nil, ErrNoSuchNetwork(id) } - return nil, ErrNoSuchNetwork(id) + + return n, nil } // NewSandbox creates a new sandbox for the passed container id @@ -456,6 +508,18 @@ func (c *controller) NewSandbox(containerID string, options ...SandboxOption) (S c.Lock() c.sandboxes[sb.id] = sb c.Unlock() + defer func() { + if err != nil { + c.Lock() + delete(c.sandboxes, sb.id) + c.Unlock() + } + }() + + err = sb.storeUpdate() + if err != nil { + return nil, fmt.Errorf("updating the store state of sandbox failed: %v", err) + } return sb, nil } @@ -534,20 +598,43 @@ func (c *controller) loadDriver(networkType string) (*driverData, error) { return dd, nil } -func (c *controller) getDriver(networkType string) (*driverData, error) { - c.Lock() - defer c.Unlock() - dd, ok := c.drivers[networkType] - if !ok { - return nil, types.NotFoundErrorf("driver %s not found", networkType) +func (c *controller) loadIpamDriver(name string) (*ipamData, error) { + if _, err := plugins.Get(name, ipamapi.PluginEndpointType); err != nil { + if err == plugins.ErrNotFound { + return nil, types.NotFoundErrorf(err.Error()) + } + return nil, err } - return dd, nil + c.Lock() + id, ok := c.ipamDrivers[name] + c.Unlock() + if !ok { + return nil, ErrInvalidNetworkDriver(name) + } + return id, nil +} + +func (c *controller) getIPAM(name string) (id *ipamData, err error) { + var ok bool + c.Lock() + id, ok = c.ipamDrivers[name] + c.Unlock() + if !ok { + id, err = c.loadIpamDriver(name) + } + return id, err +} + +func (c *controller) getIpamDriver(name string) (ipamapi.Ipam, error) { + id, err := c.getIPAM(name) + if err != nil { + return nil, err + } + return id.driver, nil } func (c *controller) Stop() { - if c.localStore != nil { - c.localStore.KVStore().Close() - } + c.closeStores() c.stopExternalKeyListener() osl.GC() } diff --git a/vendor/src/github.com/docker/libnetwork/datastore/cache.go b/vendor/src/github.com/docker/libnetwork/datastore/cache.go new file mode 100644 index 0000000000..08c8ac4839 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/datastore/cache.go @@ -0,0 +1,153 @@ +package datastore + +import ( + "fmt" + "sync" + + "github.com/docker/libkv/store" + "github.com/docker/libkv/store/boltdb" +) + +type kvMap map[string]KVObject + +type cache struct { + sync.Mutex + kmm map[string]kvMap + ds *datastore +} + +func newCache(ds *datastore) *cache { + return &cache{kmm: make(map[string]kvMap), ds: ds} +} + +func (c *cache) kmap(kvObject KVObject) (kvMap, error) { + var err error + + c.Lock() + keyPrefix := Key(kvObject.KeyPrefix()...) + kmap, ok := c.kmm[keyPrefix] + c.Unlock() + + if ok { + return kmap, nil + } + + kmap = kvMap{} + + // Bail out right away if the kvObject does not implement KVConstructor + ctor, ok := kvObject.(KVConstructor) + if !ok { + return nil, fmt.Errorf("error while populating kmap, object does not implement KVConstructor interface") + } + + kvList, err := c.ds.store.List(keyPrefix) + if err != nil { + // In case of BoltDB it may return ErrBoltBucketNotFound when no writes + // have ever happened on the db bucket. So check for both err codes + if err == store.ErrKeyNotFound || err == boltdb.ErrBoltBucketNotFound { + // If the store doesn't have anything then there is nothing to + // populate in the cache. Just bail out. + goto out + } + + return nil, fmt.Errorf("error while populating kmap: %v", err) + } + + for _, kvPair := range kvList { + // Ignore empty kvPair values + if len(kvPair.Value) == 0 { + continue + } + + dstO := ctor.New() + err = dstO.SetValue(kvPair.Value) + if err != nil { + return nil, err + } + + // Make sure the object has a correct view of the DB index in + // case we need to modify it and update the DB. + dstO.SetIndex(kvPair.LastIndex) + + kmap[Key(dstO.Key()...)] = dstO + } + +out: + // There may multiple go routines racing to fill the + // cache. The one which places the kmap in c.kmm first + // wins. The others should just use what the first populated. + c.Lock() + kmapNew, ok := c.kmm[keyPrefix] + if ok { + c.Unlock() + return kmapNew, nil + } + + c.kmm[keyPrefix] = kmap + c.Unlock() + + return kmap, nil +} + +func (c *cache) add(kvObject KVObject) error { + kmap, err := c.kmap(kvObject) + if err != nil { + return err + } + + c.Lock() + kmap[Key(kvObject.Key()...)] = kvObject + c.Unlock() + return nil +} + +func (c *cache) del(kvObject KVObject) error { + kmap, err := c.kmap(kvObject) + if err != nil { + return err + } + + c.Lock() + delete(kmap, Key(kvObject.Key()...)) + c.Unlock() + return nil +} + +func (c *cache) get(key string, kvObject KVObject) error { + kmap, err := c.kmap(kvObject) + if err != nil { + return err + } + + c.Lock() + defer c.Unlock() + + o, ok := kmap[Key(kvObject.Key()...)] + if !ok { + return ErrKeyNotFound + } + + ctor, ok := o.(KVConstructor) + if !ok { + return fmt.Errorf("kvobject does not implement KVConstructor interface. could not get object") + } + + return ctor.CopyTo(kvObject) +} + +func (c *cache) list(kvObject KVObject) ([]KVObject, error) { + kmap, err := c.kmap(kvObject) + if err != nil { + return nil, err + } + + c.Lock() + defer c.Unlock() + + var kvol []KVObject + for _, v := range kmap { + kvol = append(kvol, v) + } + + return kvol, nil +} diff --git a/vendor/src/github.com/docker/libnetwork/datastore/datastore.go b/vendor/src/github.com/docker/libnetwork/datastore/datastore.go index 927b67d23e..027fe48df3 100644 --- a/vendor/src/github.com/docker/libnetwork/datastore/datastore.go +++ b/vendor/src/github.com/docker/libnetwork/datastore/datastore.go @@ -1,8 +1,11 @@ package datastore import ( + "fmt" + "log" "reflect" "strings" + "sync" "github.com/docker/libkv" "github.com/docker/libkv/store" @@ -10,26 +13,37 @@ import ( "github.com/docker/libkv/store/consul" "github.com/docker/libkv/store/etcd" "github.com/docker/libkv/store/zookeeper" - "github.com/docker/libnetwork/config" "github.com/docker/libnetwork/types" ) //DataStore exported type DataStore interface { // GetObject gets data from datastore and unmarshals to the specified object - GetObject(key string, o KV) error + GetObject(key string, o KVObject) error // PutObject adds a new Record based on an object into the datastore - PutObject(kvObject KV) error + PutObject(kvObject KVObject) error // PutObjectAtomic provides an atomic add and update operation for a Record - PutObjectAtomic(kvObject KV) error + PutObjectAtomic(kvObject KVObject) error // DeleteObject deletes a record - DeleteObject(kvObject KV) error + DeleteObject(kvObject KVObject) error // DeleteObjectAtomic performs an atomic delete operation - DeleteObjectAtomic(kvObject KV) error + DeleteObjectAtomic(kvObject KVObject) error // DeleteTree deletes a record - DeleteTree(kvObject KV) error + DeleteTree(kvObject KVObject) error + // Watchable returns whether the store is watchable are not + Watchable() bool + // Watch for changes on a KVObject + Watch(kvObject KVObject, stopCh <-chan struct{}) (<-chan KVObject, error) + // List returns of a list of KVObjects belonging to the parent + // key. The caller must pass a KVObject of the same type as + // the objects that need to be listed + List(string, KVObject) ([]KVObject, error) + // Scope returns the scope of the store + Scope() string // KVStore returns access to the KV Store KVStore() store.Store + // Close closes the data store + Close() } // ErrKeyModified is raised for an atomic update when the update is working on a stale state @@ -39,11 +53,14 @@ var ( ) type datastore struct { + scope string store store.Store + cache *cache + sync.Mutex } -//KV Key Value interface used by objects to be part of the DataStore -type KV interface { +// KVObject is Key/Value interface used by objects to be part of the DataStore +type KVObject interface { // Key method lets an object to provide the Key to be used in KV Store Key() []string // KeyPrefix method lets an object to return immediate parent key that can be used for tree walk @@ -60,19 +77,39 @@ type KV interface { // When SetIndex() is called, the object has been stored. Exists() bool // DataScope indicates the storage scope of the KV object - DataScope() DataScope + DataScope() string // Skip provides a way for a KV Object to avoid persisting it in the KV Store Skip() bool } -// DataScope indicates the storage scope -type DataScope int +// KVConstructor interface defines methods which can construct a KVObject from another. +type KVConstructor interface { + // New returns a new object which is created based on the + // source object + New() KVObject + // CopyTo deep copies the contents of the implementing object + // to the passed destination object + CopyTo(KVObject) error +} + +// ScopeCfg represents Datastore configuration. +type ScopeCfg struct { + Client ScopeClientCfg +} + +// ScopeClientCfg represents Datastore Client-only mode configuration +type ScopeClientCfg struct { + Provider string + Address string + Config *store.Config +} const ( // LocalScope indicates to store the KV object in local datastore such as boltdb - LocalScope DataScope = iota + LocalScope = "local" // GlobalScope indicates to store the KV object in global datastore such as consul/etcd/zookeeper - GlobalScope + GlobalScope = "global" + defaultPrefix = "/var/lib/docker/network/files" ) const ( @@ -82,7 +119,26 @@ const ( EndpointKeyPrefix = "endpoint" ) -var rootChain = []string{"docker", "libnetwork"} +var ( + defaultScopes = makeDefaultScopes() +) + +func makeDefaultScopes() map[string]*ScopeCfg { + def := make(map[string]*ScopeCfg) + def[LocalScope] = &ScopeCfg{ + Client: ScopeClientCfg{ + Provider: "boltdb", + Address: defaultPrefix + "/local-kv.db", + Config: &store.Config{ + Bucket: "libnetwork", + }, + }, + } + + return def +} + +var rootChain = []string{"docker", "network", "v1.0"} func init() { consul.Register() @@ -91,6 +147,28 @@ func init() { boltdb.Register() } +// DefaultScopes returns a map of default scopes and it's config for clients to use. +func DefaultScopes(dataDir string) map[string]*ScopeCfg { + if dataDir != "" { + defaultScopes[LocalScope].Client.Address = dataDir + "/network/files/local-kv.db" + return defaultScopes + } + + defaultScopes[LocalScope].Client.Address = defaultPrefix + "/local-kv.db" + return defaultScopes +} + +// IsValid checks if the scope config has valid configuration. +func (cfg *ScopeCfg) IsValid() bool { + if cfg == nil || + strings.TrimSpace(cfg.Client.Provider) == "" || + strings.TrimSpace(cfg.Client.Address) == "" { + return false + } + + return true +} + //Key provides convenient method to create a Key func Key(key ...string) string { keychain := append(rootChain, key...) @@ -110,7 +188,11 @@ func ParseKey(key string) ([]string, error) { } // newClient used to connect to KV Store -func newClient(kv string, addrs string, config *store.Config) (DataStore, error) { +func newClient(scope string, kv string, addrs string, config *store.Config, cached bool) (DataStore, error) { + if cached && scope != LocalScope { + return nil, fmt.Errorf("caching supported only for scope %s", LocalScope) + } + if config == nil { config = &store.Config{} } @@ -118,22 +200,82 @@ func newClient(kv string, addrs string, config *store.Config) (DataStore, error) if err != nil { return nil, err } - ds := &datastore{store: store} + + ds := &datastore{scope: scope, store: store} + if cached { + ds.cache = newCache(ds) + } + return ds, nil } // NewDataStore creates a new instance of LibKV data store -func NewDataStore(cfg *config.DatastoreCfg) (DataStore, error) { - if cfg == nil { - return nil, types.BadRequestErrorf("invalid configuration passed to datastore") +func NewDataStore(scope string, cfg *ScopeCfg) (DataStore, error) { + if cfg == nil || cfg.Client.Provider == "" || cfg.Client.Address == "" { + c, ok := defaultScopes[scope] + if !ok || c.Client.Provider == "" || c.Client.Address == "" { + return nil, fmt.Errorf("unexpected scope %s without configuration passed", scope) + } + + cfg = c } - // TODO : cfg.Embedded case - return newClient(cfg.Client.Provider, cfg.Client.Address, cfg.Client.Config) + + var cached bool + if scope == LocalScope { + cached = true + } + + return newClient(scope, cfg.Client.Provider, cfg.Client.Address, cfg.Client.Config, cached) } -// NewCustomDataStore can be used by clients to plugin cusom datatore that adhers to store.Store -func NewCustomDataStore(customStore store.Store) DataStore { - return &datastore{store: customStore} +func (ds *datastore) Close() { + ds.store.Close() +} + +func (ds *datastore) Scope() string { + return ds.scope +} + +func (ds *datastore) Watchable() bool { + return ds.scope != LocalScope +} + +func (ds *datastore) Watch(kvObject KVObject, stopCh <-chan struct{}) (<-chan KVObject, error) { + sCh := make(chan struct{}) + + ctor, ok := kvObject.(KVConstructor) + if !ok { + return nil, fmt.Errorf("error watching object type %T, object does not implement KVConstructor interface", kvObject) + } + + kvpCh, err := ds.store.Watch(Key(kvObject.Key()...), sCh) + if err != nil { + return nil, err + } + + kvoCh := make(chan KVObject) + + go func() { + for { + select { + case <-stopCh: + close(sCh) + return + case kvPair := <-kvpCh: + dstO := ctor.New() + + if err := dstO.SetValue(kvPair.Value); err != nil { + log.Printf("Could not unmarshal kvpair value = %s", string(kvPair.Value)) + break + } + + dstO.SetIndex(kvPair.LastIndex) + kvoCh <- dstO + } + } + }() + + return kvoCh, nil } func (ds *datastore) KVStore() store.Store { @@ -141,40 +283,76 @@ func (ds *datastore) KVStore() store.Store { } // PutObjectAtomic adds a new Record based on an object into the datastore -func (ds *datastore) PutObjectAtomic(kvObject KV) error { +func (ds *datastore) PutObjectAtomic(kvObject KVObject) error { + var ( + previous *store.KVPair + pair *store.KVPair + err error + ) + ds.Lock() + defer ds.Unlock() + if kvObject == nil { return types.BadRequestErrorf("invalid KV Object : nil") } + kvObjValue := kvObject.Value() if kvObjValue == nil { return types.BadRequestErrorf("invalid KV Object with a nil Value for key %s", Key(kvObject.Key()...)) } - var previous *store.KVPair + if kvObject.Skip() { + goto add_cache + } + if kvObject.Exists() { previous = &store.KVPair{Key: Key(kvObject.Key()...), LastIndex: kvObject.Index()} } else { previous = nil } - _, pair, err := ds.store.AtomicPut(Key(kvObject.Key()...), kvObjValue, previous, nil) + + _, pair, err = ds.store.AtomicPut(Key(kvObject.Key()...), kvObjValue, previous, nil) if err != nil { return err } kvObject.SetIndex(pair.LastIndex) + +add_cache: + if ds.cache != nil { + return ds.cache.add(kvObject) + } + return nil } // PutObject adds a new Record based on an object into the datastore -func (ds *datastore) PutObject(kvObject KV) error { +func (ds *datastore) PutObject(kvObject KVObject) error { + ds.Lock() + defer ds.Unlock() + if kvObject == nil { return types.BadRequestErrorf("invalid KV Object : nil") } - return ds.putObjectWithKey(kvObject, kvObject.Key()...) + + if kvObject.Skip() { + goto add_cache + } + + if err := ds.putObjectWithKey(kvObject, kvObject.Key()...); err != nil { + return err + } + +add_cache: + if ds.cache != nil { + return ds.cache.add(kvObject) + } + + return nil } -func (ds *datastore) putObjectWithKey(kvObject KV, key ...string) error { +func (ds *datastore) putObjectWithKey(kvObject KVObject, key ...string) error { kvObjValue := kvObject.Value() if kvObjValue == nil { @@ -184,39 +362,143 @@ func (ds *datastore) putObjectWithKey(kvObject KV, key ...string) error { } // GetObject returns a record matching the key -func (ds *datastore) GetObject(key string, o KV) error { +func (ds *datastore) GetObject(key string, o KVObject) error { + ds.Lock() + defer ds.Unlock() + + if ds.cache != nil { + return ds.cache.get(key, o) + } + kvPair, err := ds.store.Get(key) if err != nil { return err } - err = o.SetValue(kvPair.Value) - if err != nil { + + if err := o.SetValue(kvPair.Value); err != nil { return err } - // Make sure the object has a correct view of the DB index in case we need to modify it - // and update the DB. + // Make sure the object has a correct view of the DB index in + // case we need to modify it and update the DB. o.SetIndex(kvPair.LastIndex) return nil } +func (ds *datastore) ensureKey(key string) error { + exists, err := ds.store.Exists(key) + if err != nil { + return err + } + if exists { + return nil + } + return ds.store.Put(key, []byte{}, nil) +} + +func (ds *datastore) List(key string, kvObject KVObject) ([]KVObject, error) { + ds.Lock() + defer ds.Unlock() + + if ds.cache != nil { + return ds.cache.list(kvObject) + } + + // Bail out right away if the kvObject does not implement KVConstructor + ctor, ok := kvObject.(KVConstructor) + if !ok { + return nil, fmt.Errorf("error listing objects, object does not implement KVConstructor interface") + } + + // Make sure the parent key exists + if err := ds.ensureKey(key); err != nil { + return nil, err + } + + kvList, err := ds.store.List(key) + if err != nil { + return nil, err + } + + var kvol []KVObject + for _, kvPair := range kvList { + if len(kvPair.Value) == 0 { + continue + } + + dstO := ctor.New() + if err := dstO.SetValue(kvPair.Value); err != nil { + return nil, err + } + + // Make sure the object has a correct view of the DB index in + // case we need to modify it and update the DB. + dstO.SetIndex(kvPair.LastIndex) + + kvol = append(kvol, dstO) + } + + return kvol, nil +} + // DeleteObject unconditionally deletes a record from the store -func (ds *datastore) DeleteObject(kvObject KV) error { +func (ds *datastore) DeleteObject(kvObject KVObject) error { + ds.Lock() + defer ds.Unlock() + + // cleaup the cache first + if ds.cache != nil { + ds.cache.del(kvObject) + } + + if kvObject.Skip() { + return nil + } + return ds.store.Delete(Key(kvObject.Key()...)) } // DeleteObjectAtomic performs atomic delete on a record -func (ds *datastore) DeleteObjectAtomic(kvObject KV) error { +func (ds *datastore) DeleteObjectAtomic(kvObject KVObject) error { + ds.Lock() + defer ds.Unlock() + if kvObject == nil { return types.BadRequestErrorf("invalid KV Object : nil") } previous := &store.KVPair{Key: Key(kvObject.Key()...), LastIndex: kvObject.Index()} - _, err := ds.store.AtomicDelete(Key(kvObject.Key()...), previous) - return err + + if kvObject.Skip() { + goto del_cache + } + + if _, err := ds.store.AtomicDelete(Key(kvObject.Key()...), previous); err != nil { + return err + } + +del_cache: + // cleanup the cache only if AtomicDelete went through successfully + if ds.cache != nil { + return ds.cache.del(kvObject) + } + + return nil } // DeleteTree unconditionally deletes a record from the store -func (ds *datastore) DeleteTree(kvObject KV) error { +func (ds *datastore) DeleteTree(kvObject KVObject) error { + ds.Lock() + defer ds.Unlock() + + // cleaup the cache first + if ds.cache != nil { + ds.cache.del(kvObject) + } + + if kvObject.Skip() { + return nil + } + return ds.store.DeleteTree(Key(kvObject.KeyPrefix()...)) } diff --git a/vendor/src/github.com/docker/libnetwork/default_gateway.go b/vendor/src/github.com/docker/libnetwork/default_gateway.go index 173d926905..98cc21f73d 100644 --- a/vendor/src/github.com/docker/libnetwork/default_gateway.go +++ b/vendor/src/github.com/docker/libnetwork/default_gateway.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/docker/libnetwork/netlabel" - "github.com/docker/libnetwork/options" "github.com/docker/libnetwork/types" ) @@ -92,25 +91,6 @@ func (sb *sandbox) clearDefaultGW() error { return nil } -func (c *controller) createGWNetwork() (Network, error) { - netOption := options.Generic{ - "BridgeName": libnGWNetwork, - "EnableICC": false, - "EnableIPMasquerade": true, - } - - n, err := c.NewNetwork("bridge", libnGWNetwork, - NetworkOptionGeneric(options.Generic{ - netlabel.GenericData: netOption, - netlabel.EnableIPv6: false, - })) - - if err != nil { - return nil, fmt.Errorf("error creating external connectivity network: %v", err) - } - return n, err -} - func (sb *sandbox) needDefaultGW() bool { var needGW bool diff --git a/vendor/src/github.com/docker/libnetwork/default_gateway_freebsd.go b/vendor/src/github.com/docker/libnetwork/default_gateway_freebsd.go new file mode 100644 index 0000000000..a82687391a --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/default_gateway_freebsd.go @@ -0,0 +1,7 @@ +package libnetwork + +import "github.com/docker/libnetwork/types" + +func (c *controller) createGWNetwork() (Network, error) { + return nil, types.NotImplementedErrorf("default gateway functionality is not implemented in freebsd") +} diff --git a/vendor/src/github.com/docker/libnetwork/default_gateway_linux.go b/vendor/src/github.com/docker/libnetwork/default_gateway_linux.go new file mode 100644 index 0000000000..c7f812778f --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/default_gateway_linux.go @@ -0,0 +1,29 @@ +package libnetwork + +import ( + "fmt" + "strconv" + + "github.com/docker/libnetwork/drivers/bridge" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/options" +) + +func (c *controller) createGWNetwork() (Network, error) { + netOption := map[string]string{ + bridge.BridgeName: libnGWNetwork, + bridge.EnableICC: strconv.FormatBool(false), + bridge.EnableIPMasquerade: strconv.FormatBool(true), + } + + n, err := c.NewNetwork("bridge", libnGWNetwork, + NetworkOptionGeneric(options.Generic{ + netlabel.GenericData: netOption, + netlabel.EnableIPv6: false, + })) + + if err != nil { + return nil, fmt.Errorf("error creating external connectivity network: %v", err) + } + return n, err +} diff --git a/vendor/src/github.com/docker/libnetwork/default_gateway_windows.go b/vendor/src/github.com/docker/libnetwork/default_gateway_windows.go new file mode 100644 index 0000000000..48eee2ba1d --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/default_gateway_windows.go @@ -0,0 +1,7 @@ +package libnetwork + +import "github.com/docker/libnetwork/types" + +func (c *controller) createGWNetwork() (Network, error) { + return nil, types.NotImplementedErrorf("default gateway functionality is not implemented in windows") +} diff --git a/vendor/src/github.com/docker/libnetwork/driverapi/driverapi.go b/vendor/src/github.com/docker/libnetwork/driverapi/driverapi.go index 1661c3672f..bd311d0035 100644 --- a/vendor/src/github.com/docker/libnetwork/driverapi/driverapi.go +++ b/vendor/src/github.com/docker/libnetwork/driverapi/driverapi.go @@ -1,10 +1,6 @@ package driverapi -import ( - "net" - - "github.com/docker/libnetwork/datastore" -) +import "net" // NetworkPluginEndpointType represents the Endpoint Type used by Plugin system const NetworkPluginEndpointType = "NetworkDriver" @@ -14,7 +10,7 @@ type Driver interface { // CreateNetwork invokes the driver method to create a network passing // the network id and network specific config. The config mechanism will // eventually be replaced with labels which are yet to be introduced. - CreateNetwork(nid string, options map[string]interface{}) error + CreateNetwork(nid string, options map[string]interface{}, ipV4Data, ipV6Data []IPAMData) error // DeleteNetwork invokes the driver method to delete network passing // the network id. @@ -25,7 +21,7 @@ type Driver interface { // specific config. The endpoint information can be either consumed by // the driver or populated by the driver. The config mechanism will // eventually be replaced with labels which are yet to be introduced. - CreateEndpoint(nid, eid string, epInfo EndpointInfo, options map[string]interface{}) error + CreateEndpoint(nid, eid string, ifInfo InterfaceInfo, options map[string]interface{}) error // DeleteEndpoint invokes the driver method to delete an endpoint // passing the network id and endpoint id. @@ -50,31 +46,26 @@ type Driver interface { Type() string } -// EndpointInfo provides a go interface to fetch or populate endpoint assigned network resources. -type EndpointInfo interface { - // Interface returns the interface bound to the endpoint. - // If the value is not nil the driver is only expected to consume the interface. - // It is an error to try to add interface if the passed down value is non-nil - // If the value is nil the driver is expected to add an interface - Interface() InterfaceInfo - - // AddInterface is used by the driver to add an interface for the endpoint. - // This method will return an error if the driver attempts to add interface - // if the Interface() method returned a non-nil value. - AddInterface(mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error -} - // InterfaceInfo provides a go interface for drivers to retrive // network information to interface resources. type InterfaceInfo interface { + // SetMacAddress allows the driver to set the mac address to the endpoint interface + // during the call to CreateEndpoint, if the mac address is not already set. + SetMacAddress(mac net.HardwareAddr) error + + // SetIPAddress allows the driver to set the ip address to the endpoint interface + // during the call to CreateEndpoint, if the address is not already set. + // The API is to be used to assign both the IPv4 and IPv6 address types. + SetIPAddress(ip *net.IPNet) error + // MacAddress returns the MAC address. MacAddress() net.HardwareAddr // Address returns the IPv4 address. - Address() net.IPNet + Address() *net.IPNet // AddressIPv6 returns the IPv6 address. - AddressIPv6() net.IPNet + AddressIPv6() *net.IPNet } // InterfaceNameInfo provides a go interface for the drivers to assign names @@ -110,7 +101,7 @@ type DriverCallback interface { // Capability represents the high level capabilities of the drivers which libnetwork can make use of type Capability struct { - DataScope datastore.DataScope + DataScope string } // DiscoveryType represents the type of discovery element the DiscoverNew function is invoked on @@ -126,3 +117,13 @@ type NodeDiscoveryData struct { Address string Self bool } + +// IPAMData represents the per-network ip related +// operational information libnetwork will send +// to the network driver during CreateNetwork() +type IPAMData struct { + AddressSpace string + Pool *net.IPNet + Gateway *net.IPNet + AuxAddresses map[string]*net.IPNet +} diff --git a/vendor/src/github.com/docker/libnetwork/driverapi/ipamdata.go b/vendor/src/github.com/docker/libnetwork/driverapi/ipamdata.go new file mode 100644 index 0000000000..9a2375bf8a --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/driverapi/ipamdata.go @@ -0,0 +1,103 @@ +package driverapi + +import ( + "encoding/json" + "fmt" + "net" + + "github.com/docker/libnetwork/types" +) + +// MarshalJSON encodes IPAMData into json message +func (i *IPAMData) MarshalJSON() ([]byte, error) { + m := map[string]interface{}{} + m["AddressSpace"] = i.AddressSpace + if i.Pool != nil { + m["Pool"] = i.Pool.String() + } + if i.Gateway != nil { + m["Gateway"] = i.Gateway.String() + } + if i.AuxAddresses != nil { + am := make(map[string]string, len(i.AuxAddresses)) + for k, v := range i.AuxAddresses { + am[k] = v.String() + } + m["AuxAddresses"] = am + } + return json.Marshal(m) +} + +// UnmarshalJSON decodes a json message into IPAMData +func (i *IPAMData) UnmarshalJSON(data []byte) error { + var ( + m map[string]interface{} + err error + ) + if err := json.Unmarshal(data, &m); err != nil { + return err + } + i.AddressSpace = m["AddressSpace"].(string) + if v, ok := m["Pool"]; ok { + if i.Pool, err = types.ParseCIDR(v.(string)); err != nil { + return err + } + } + if v, ok := m["Gateway"]; ok { + if i.Gateway, err = types.ParseCIDR(v.(string)); err != nil { + return err + } + } + if v, ok := m["AuxAddresses"]; ok { + b, _ := json.Marshal(v) + var am map[string]string + if err = json.Unmarshal(b, &am); err != nil { + return err + } + i.AuxAddresses = make(map[string]*net.IPNet, len(am)) + for k, v := range am { + if i.AuxAddresses[k], err = types.ParseCIDR(v); err != nil { + return err + } + } + } + return nil +} + +// Validate checks wheter the IPAMData structure contains congruent data +func (i *IPAMData) Validate() error { + var isV6 bool + if i.Pool == nil { + return types.BadRequestErrorf("invalid pool") + } + if i.Gateway == nil { + return types.BadRequestErrorf("invalid gateway address") + } + isV6 = i.IsV6() + if isV6 && i.Gateway.IP.To4() != nil || !isV6 && i.Gateway.IP.To4() == nil { + return types.BadRequestErrorf("incongruent ip versions for pool and gateway") + } + for k, sip := range i.AuxAddresses { + if isV6 && sip.IP.To4() != nil || !isV6 && sip.IP.To4() == nil { + return types.BadRequestErrorf("incongruent ip versions for pool and secondary ip address %s", k) + } + } + if !i.Pool.Contains(i.Gateway.IP) { + return types.BadRequestErrorf("invalid gateway address (%s) does not belong to the pool (%s)", i.Gateway, i.Pool) + } + for k, sip := range i.AuxAddresses { + if !i.Pool.Contains(sip.IP) { + return types.BadRequestErrorf("invalid secondary address %s (%s) does not belong to the pool (%s)", k, i.Gateway, i.Pool) + } + } + return nil +} + +// IsV6 returns wheter this is an IPv6 IPAMData structure +func (i *IPAMData) IsV6() bool { + return nil == i.Pool.IP.To4() +} + +func (i *IPAMData) String() string { + return fmt.Sprintf("AddressSpace: %s\nPool: %v\nGateway: %v\nAddresses: %v", i.AddressSpace, i.Pool, i.Gateway, i.AuxAddresses) +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers.go b/vendor/src/github.com/docker/libnetwork/drivers.go index 898bcc4ebe..1b11f37324 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers.go +++ b/vendor/src/github.com/docker/libnetwork/drivers.go @@ -4,6 +4,9 @@ import ( "strings" "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/ipamapi" + builtinIpam "github.com/docker/libnetwork/ipams/builtin" + remoteIpam "github.com/docker/libnetwork/ipams/remote" "github.com/docker/libnetwork/netlabel" ) @@ -29,11 +32,6 @@ func makeDriverConfig(c *controller, ntype string) map[string]interface{} { config := make(map[string]interface{}) - if c.validateGlobalStoreConfig() { - config[netlabel.KVProvider] = c.cfg.GlobalStore.Client.Provider - config[netlabel.KVProviderURL] = c.cfg.GlobalStore.Client.Address - } - for _, label := range c.cfg.Daemon.Labels { if !strings.HasPrefix(netlabel.Key(label), netlabel.DriverPrefix+"."+ntype) { continue @@ -43,13 +41,38 @@ func makeDriverConfig(c *controller, ntype string) map[string]interface{} { } drvCfg, ok := c.cfg.Daemon.DriverCfg[ntype] - if !ok { + if ok { + for k, v := range drvCfg.(map[string]interface{}) { + config[k] = v + } + } + + // We don't send datastore configs to external plugins + if ntype == "remote" { return config } - for k, v := range drvCfg.(map[string]interface{}) { - config[k] = v + for k, v := range c.cfg.Scopes { + if !v.IsValid() { + continue + } + + config[netlabel.MakeKVProvider(k)] = v.Client.Provider + config[netlabel.MakeKVProviderURL(k)] = v.Client.Address + config[netlabel.MakeKVProviderConfig(k)] = v.Client.Config } return config } + +func initIpams(ic ipamapi.Callback, lDs, gDs interface{}) error { + for _, fn := range [](func(ipamapi.Callback, interface{}, interface{}) error){ + builtinIpam.Init, + remoteIpam.Init, + } { + if err := fn(ic, lDs, gDs); err != nil { + return err + } + } + return nil +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge.go index 71cc0e7fce..ecfd5d14c5 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge.go @@ -16,7 +16,6 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/libnetwork/datastore" "github.com/docker/libnetwork/driverapi" - "github.com/docker/libnetwork/ipallocator" "github.com/docker/libnetwork/iptables" "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/netutils" @@ -35,8 +34,11 @@ const ( maxAllocatePortAttempts = 10 ) -var ( - ipAllocator *ipallocator.IPAllocator +const ( + // DefaultGatewayV4AuxKey represents the default-gateway configured by the user + DefaultGatewayV4AuxKey = "DefaultGatewayIPv4" + // DefaultGatewayV6AuxKey represents the ipv6 default-gateway configured by the user + DefaultGatewayV6AuxKey = "DefaultGatewayIPv6" ) // configuration info for the "bridge" driver. @@ -48,18 +50,21 @@ type configuration struct { // networkConfiguration for network specific configuration type networkConfiguration struct { + ID string BridgeName string - AddressIPv4 *net.IPNet - FixedCIDR *net.IPNet - FixedCIDRv6 *net.IPNet EnableIPv6 bool EnableIPMasquerade bool EnableICC bool Mtu int - DefaultGatewayIPv4 net.IP - DefaultGatewayIPv6 net.IP DefaultBindingIP net.IP DefaultBridge bool + // Internal fields set after ipam data parsing + AddressIPv4 *net.IPNet + AddressIPv6 *net.IPNet + DefaultGatewayIPv4 net.IP + DefaultGatewayIPv6 net.IP + dbIndex uint64 + dbExists bool } // endpointConfiguration represents the user specified configuration for the sandbox endpoint @@ -102,12 +107,12 @@ type driver struct { natChain *iptables.ChainInfo filterChain *iptables.ChainInfo networks map[string]*bridgeNetwork + store datastore.DataStore sync.Mutex } // New constructs a new bridge driver func newDriver() *driver { - ipAllocator = ipallocator.New() return &driver{networks: map[string]*bridgeNetwork{}, config: &configuration{}} } @@ -148,19 +153,6 @@ func (c *networkConfiguration) Validate() error { // If bridge v4 subnet is specified if c.AddressIPv4 != nil { - // If Container restricted subnet is specified, it must be a subset of bridge subnet - if c.FixedCIDR != nil { - // Check Network address - if !c.AddressIPv4.Contains(c.FixedCIDR.IP) { - return &ErrInvalidContainerSubnet{} - } - // Check it is effectively a subset - brNetLen, _ := c.AddressIPv4.Mask.Size() - cnNetLen, _ := c.FixedCIDR.Mask.Size() - if brNetLen > cnNetLen { - return &ErrInvalidContainerSubnet{} - } - } // If default gw is specified, it must be part of bridge subnet if c.DefaultGatewayIPv4 != nil { if !c.AddressIPv4.Contains(c.DefaultGatewayIPv4) { @@ -169,167 +161,81 @@ func (c *networkConfiguration) Validate() error { } } - // If default v6 gw is specified, FixedCIDRv6 must be specified and gw must belong to FixedCIDRv6 subnet + // If default v6 gw is specified, AddressIPv6 must be specified and gw must belong to AddressIPv6 subnet if c.EnableIPv6 && c.DefaultGatewayIPv6 != nil { - if c.FixedCIDRv6 == nil || !c.FixedCIDRv6.Contains(c.DefaultGatewayIPv6) { + if c.AddressIPv6 == nil || !c.AddressIPv6.Contains(c.DefaultGatewayIPv6) { return &ErrInvalidGateway{} } } - return nil } // Conflicts check if two NetworkConfiguration objects overlap -func (c *networkConfiguration) Conflicts(o *networkConfiguration) bool { +func (c *networkConfiguration) Conflicts(o *networkConfiguration) error { if o == nil { - return false + return fmt.Errorf("same configuration") } // Also empty, becasue only one network with empty name is allowed if c.BridgeName == o.BridgeName { - return true + return fmt.Errorf("networks have same bridge name") } // They must be in different subnets if (c.AddressIPv4 != nil && o.AddressIPv4 != nil) && (c.AddressIPv4.Contains(o.AddressIPv4.IP) || o.AddressIPv4.Contains(c.AddressIPv4.IP)) { - return true + return fmt.Errorf("networks have overlapping IPv4") } - return false + // They must be in different v6 subnets + if (c.AddressIPv6 != nil && o.AddressIPv6 != nil) && + (c.AddressIPv6.Contains(o.AddressIPv6.IP) || o.AddressIPv6.Contains(c.AddressIPv6.IP)) { + return fmt.Errorf("networks have overlapping IPv6") + } + + return nil } -// fromMap retrieve the configuration data from the map form. -func (c *networkConfiguration) fromMap(data map[string]interface{}) error { +func (c *networkConfiguration) fromLabels(labels map[string]string) error { var err error - - if i, ok := data["BridgeName"]; ok && i != nil { - if c.BridgeName, ok = i.(string); !ok { - return types.BadRequestErrorf("invalid type for BridgeName value") - } - } - - if i, ok := data["Mtu"]; ok && i != nil { - if s, ok := i.(string); ok { - if c.Mtu, err = strconv.Atoi(s); err != nil { - return types.BadRequestErrorf("failed to parse Mtu value: %s", err.Error()) + for label, value := range labels { + switch label { + case BridgeName: + c.BridgeName = value + case netlabel.DriverMTU: + if c.Mtu, err = strconv.Atoi(value); err != nil { + return parseErr(label, value, err.Error()) + } + case netlabel.EnableIPv6: + if c.EnableIPv6, err = strconv.ParseBool(value); err != nil { + return parseErr(label, value, err.Error()) + } + case EnableIPMasquerade: + if c.EnableIPMasquerade, err = strconv.ParseBool(value); err != nil { + return parseErr(label, value, err.Error()) + } + case EnableICC: + if c.EnableICC, err = strconv.ParseBool(value); err != nil { + return parseErr(label, value, err.Error()) + } + case DefaultBridge: + if c.DefaultBridge, err = strconv.ParseBool(value); err != nil { + return parseErr(label, value, err.Error()) + } + case DefaultBindingIP: + if c.DefaultBindingIP = net.ParseIP(value); c.DefaultBindingIP == nil { + return parseErr(label, value, "nil ip") } - } else { - return types.BadRequestErrorf("invalid type for Mtu value") } } - if i, ok := data["EnableIPv6"]; ok && i != nil { - if s, ok := i.(string); ok { - if c.EnableIPv6, err = strconv.ParseBool(s); err != nil { - return types.BadRequestErrorf("failed to parse EnableIPv6 value: %s", err.Error()) - } - } else { - return types.BadRequestErrorf("invalid type for EnableIPv6 value") - } - } - - if i, ok := data["EnableIPMasquerade"]; ok && i != nil { - if s, ok := i.(string); ok { - if c.EnableIPMasquerade, err = strconv.ParseBool(s); err != nil { - return types.BadRequestErrorf("failed to parse EnableIPMasquerade value: %s", err.Error()) - } - } else { - return types.BadRequestErrorf("invalid type for EnableIPMasquerade value") - } - } - - if i, ok := data["EnableICC"]; ok && i != nil { - if s, ok := i.(string); ok { - if c.EnableICC, err = strconv.ParseBool(s); err != nil { - return types.BadRequestErrorf("failed to parse EnableICC value: %s", err.Error()) - } - } else { - return types.BadRequestErrorf("invalid type for EnableICC value") - } - } - - if i, ok := data["DefaultBridge"]; ok && i != nil { - if s, ok := i.(string); ok { - if c.DefaultBridge, err = strconv.ParseBool(s); err != nil { - return types.BadRequestErrorf("failed to parse DefaultBridge value: %s", err.Error()) - } - } else { - return types.BadRequestErrorf("invalid type for DefaultBridge value") - } - } - - if i, ok := data["AddressIPv4"]; ok && i != nil { - if s, ok := i.(string); ok { - if ip, nw, e := net.ParseCIDR(s); e == nil { - nw.IP = ip - c.AddressIPv4 = nw - } else { - return types.BadRequestErrorf("failed to parse AddressIPv4 value") - } - } else { - return types.BadRequestErrorf("invalid type for AddressIPv4 value") - } - } - - if i, ok := data["FixedCIDR"]; ok && i != nil { - if s, ok := i.(string); ok { - if ip, nw, e := net.ParseCIDR(s); e == nil { - nw.IP = ip - c.FixedCIDR = nw - } else { - return types.BadRequestErrorf("failed to parse FixedCIDR value") - } - } else { - return types.BadRequestErrorf("invalid type for FixedCIDR value") - } - } - - if i, ok := data["FixedCIDRv6"]; ok && i != nil { - if s, ok := i.(string); ok { - if ip, nw, e := net.ParseCIDR(s); e == nil { - nw.IP = ip - c.FixedCIDRv6 = nw - } else { - return types.BadRequestErrorf("failed to parse FixedCIDRv6 value") - } - } else { - return types.BadRequestErrorf("invalid type for FixedCIDRv6 value") - } - } - - if i, ok := data["DefaultGatewayIPv4"]; ok && i != nil { - if s, ok := i.(string); ok { - if c.DefaultGatewayIPv4 = net.ParseIP(s); c.DefaultGatewayIPv4 == nil { - return types.BadRequestErrorf("failed to parse DefaultGatewayIPv4 value") - } - } else { - return types.BadRequestErrorf("invalid type for DefaultGatewayIPv4 value") - } - } - - if i, ok := data["DefaultGatewayIPv6"]; ok && i != nil { - if s, ok := i.(string); ok { - if c.DefaultGatewayIPv6 = net.ParseIP(s); c.DefaultGatewayIPv6 == nil { - return types.BadRequestErrorf("failed to parse DefaultGatewayIPv6 value") - } - } else { - return types.BadRequestErrorf("invalid type for DefaultGatewayIPv6 value") - } - } - - if i, ok := data["DefaultBindingIP"]; ok && i != nil { - if s, ok := i.(string); ok { - if c.DefaultBindingIP = net.ParseIP(s); c.DefaultBindingIP == nil { - return types.BadRequestErrorf("failed to parse DefaultBindingIP value") - } - } else { - return types.BadRequestErrorf("invalid type for DefaultBindingIP value") - } - } return nil } +func parseErr(label, value, errString string) error { + return types.BadRequestErrorf("failed to parse %s value: %v (%s)", label, value, errString) +} + func (n *bridgeNetwork) getDriverChains() (*iptables.ChainInfo, *iptables.ChainInfo, error) { n.Lock() defer n.Unlock() @@ -435,6 +341,11 @@ func (d *driver) configure(option map[string]interface{}) error { var config *configuration var err error + err = d.initStore(option) + if err != nil { + return err + } + d.Lock() defer d.Unlock() @@ -498,12 +409,12 @@ func parseNetworkGenericOptions(data interface{}) (*networkConfiguration, error) switch opt := data.(type) { case *networkConfiguration: config = opt - case map[string]interface{}: + case map[string]string: config = &networkConfiguration{ EnableICC: true, EnableIPMasquerade: true, } - err = config.fromMap(opt) + err = config.fromLabels(opt) case options.Generic: var opaqueConfig interface{} if opaqueConfig, err = options.GenerateFromModel(opt, config); err == nil { @@ -516,9 +427,41 @@ func parseNetworkGenericOptions(data interface{}) (*networkConfiguration, error) return config, err } +func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []driverapi.IPAMData) error { + if len(ipamV4Data) > 1 || len(ipamV6Data) > 1 { + return types.ForbiddenErrorf("bridge driver doesnt support multiple subnets") + } + + if len(ipamV4Data) == 0 { + return types.BadRequestErrorf("bridge network %s requires ipv4 configuration", id) + } + + if ipamV4Data[0].Gateway != nil { + c.AddressIPv4 = types.GetIPNetCopy(ipamV4Data[0].Gateway) + } + + if gw, ok := ipamV4Data[0].AuxAddresses[DefaultGatewayV4AuxKey]; ok { + c.DefaultGatewayIPv4 = gw.IP + } + + if len(ipamV6Data) > 0 { + if ipamV6Data[0].Gateway != nil { + c.AddressIPv6 = types.GetIPNetCopy(ipamV6Data[0].Gateway) + } + + if gw, ok := ipamV6Data[0].AuxAddresses[DefaultGatewayV6AuxKey]; ok { + c.DefaultGatewayIPv6 = gw.IP + } + } + + return nil +} + func parseNetworkOptions(id string, option options.Generic) (*networkConfiguration, error) { - var err error - config := &networkConfiguration{} + var ( + err error + config = &networkConfiguration{} + ) // Parse generic label first, config will be re-assigned if genData, ok := option[netlabel.GenericData]; ok && genData != nil { @@ -528,8 +471,8 @@ func parseNetworkOptions(id string, option options.Generic) (*networkConfigurati } // Process well-known labels next - if _, ok := option[netlabel.EnableIPv6]; ok { - config.EnableIPv6 = option[netlabel.EnableIPv6].(bool) + if val, ok := option[netlabel.EnableIPv6]; ok { + config.EnableIPv6 = val.(bool) } // Finally validate the configuration @@ -540,15 +483,16 @@ func parseNetworkOptions(id string, option options.Generic) (*networkConfigurati if config.BridgeName == "" && config.DefaultBridge == false { config.BridgeName = "br-" + id[:12] } + + config.ID = id return config, nil } // Returns the non link-local IPv6 subnet for the containers attached to this bridge if found, nil otherwise func getV6Network(config *networkConfiguration, i *bridgeInterface) *net.IPNet { - if config.FixedCIDRv6 != nil { - return config.FixedCIDRv6 + if config.AddressIPv6 != nil { + return config.AddressIPv6 } - if i.bridgeIPv6 != nil && i.bridgeIPv6.IP != nil && !i.bridgeIPv6.IP.IsLinkLocalUnicast() { return i.bridgeIPv6 } @@ -569,11 +513,7 @@ func (d *driver) getNetworks() []*bridgeNetwork { } // Create a new network using bridge plugin -func (d *driver) CreateNetwork(id string, option map[string]interface{}) error { - var err error - - defer osl.InitOSContext()() - +func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error { // Sanity checks d.Lock() if _, ok := d.networks[id]; ok { @@ -587,19 +527,38 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}) error { if err != nil { return err } + + err = config.processIPAM(id, ipV4Data, ipV6Data) + if err != nil { + return err + } + + if err = d.createNetwork(config); err != nil { + return err + } + + return d.storeUpdate(config) +} + +func (d *driver) createNetwork(config *networkConfiguration) error { + var err error + + defer osl.InitOSContext()() + networkList := d.getNetworks() for _, nw := range networkList { nw.Lock() nwConfig := nw.config nw.Unlock() - if nwConfig.Conflicts(config) { - return types.ForbiddenErrorf("conflicts with network %s (%s)", nw.id, nw.config.BridgeName) + if err := nwConfig.Conflicts(config); err != nil { + return types.ForbiddenErrorf("cannot create network %s (%s): conflicts with network %s (%s): %s", + nwConfig.BridgeName, config.ID, nw.id, nw.config.BridgeName, err.Error()) } } // Create and set network handler in driver network := &bridgeNetwork{ - id: id, + id: config.ID, endpoints: make(map[string]*bridgeEndpoint), config: config, portMapper: portmapper.New(), @@ -607,14 +566,14 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}) error { } d.Lock() - d.networks[id] = network + d.networks[config.ID] = network d.Unlock() // On failure make sure to reset driver network handler to nil defer func() { if err != nil { d.Lock() - delete(d.networks, id) + delete(d.networks, config.ID) d.Unlock() } }() @@ -627,7 +586,7 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}) error { // networks. This step is needed now because driver might have now set the bridge // name on this config struct. And because we need to check for possible address // conflicts, so we need to check against operationa lnetworks. - if err = config.conflictsWithNetworks(id, networkList); err != nil { + if err = config.conflictsWithNetworks(config.ID, networkList); err != nil { return err } @@ -654,10 +613,7 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}) error { // Even if a bridge exists try to setup IPv4. bridgeSetup.queueStep(setupBridgeIPv4) - enableIPv6Forwarding := false - if d.config.EnableIPForwarding && config.FixedCIDRv6 != nil { - enableIPv6Forwarding = true - } + enableIPv6Forwarding := d.config.EnableIPForwarding && config.AddressIPv6 != nil // Conditionally queue setup steps depending on configuration values. for _, step := range []struct { @@ -674,14 +630,6 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}) error { // the case of a previously existing device. {bridgeAlreadyExists, setupVerifyAndReconcile}, - // Setup the bridge to allocate containers IPv4 addresses in the - // specified subnet. - {config.FixedCIDR != nil, setupFixedCIDRv4}, - - // Setup the bridge to allocate containers global IPv6 addresses in the - // specified subnet. - {config.FixedCIDRv6 != nil, setupFixedCIDRv6}, - // Enable IPv6 Forwarding {enableIPv6Forwarding, setupIPv6Forwarding}, @@ -712,8 +660,6 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}) error { } } - // Block bridge IP from being allocated. - bridgeSetup.queueStep(allocateBridgeIP) // Apply the prepared list of steps, and abort at the first error. bridgeSetup.queueStep(setupDeviceUp) if err = bridgeSetup.apply(); err != nil { @@ -741,10 +687,6 @@ func (d *driver) DeleteNetwork(nid string) error { config := n.config n.Unlock() - if config.BridgeName == DefaultBridgeName { - return types.ForbiddenErrorf("default network of type \"%s\" cannot be deleted", networkType) - } - d.Lock() delete(d.networks, nid) d.Unlock() @@ -789,22 +731,12 @@ func (d *driver) DeleteNetwork(nid string) error { return err } - // Programming - err = netlink.LinkDel(n.bridge.Link) - - // Release ip addresses (ignore errors) - if config.FixedCIDR == nil || config.FixedCIDR.Contains(config.DefaultGatewayIPv4) { - if e := ipAllocator.ReleaseIP(n.bridge.bridgeIPv4, n.bridge.gatewayIPv4); e != nil { - logrus.Warnf("Failed to release default gateway address %s: %v", n.bridge.gatewayIPv4.String(), e) - } - } - if config.FixedCIDR == nil || config.FixedCIDR.Contains(n.bridge.bridgeIPv4.IP) { - if e := ipAllocator.ReleaseIP(n.bridge.bridgeIPv4, n.bridge.bridgeIPv4.IP); e != nil { - logrus.Warnf("Failed to release bridge IP %s: %v", n.bridge.bridgeIPv4.IP.String(), e) - } + // We only delete the bridge when it's not the default bridge. This is keep the backward compatible behavior. + if !config.DefaultBridge { + err = netlink.LinkDel(n.bridge.Link) } - return err + return d.storeDelete(config) } func addToBridge(ifaceName, bridgeName string) error { @@ -861,20 +793,11 @@ func setHairpinMode(link netlink.Link, enable bool) error { return nil } -func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { - var ( - ipv6Addr *net.IPNet - err error - ) - +func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error { defer osl.InitOSContext()() - if epInfo == nil { - return errors.New("invalid endpoint info passed") - } - - if epInfo.Interface() != nil { - return errors.New("non-nil interface passed to bridge(local) driver") + if ifInfo == nil { + return errors.New("invalid interface info passed") } // Get the network handler and make sure it exists @@ -1000,12 +923,11 @@ func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, } } - // v4 address for the sandbox side pipe interface - ip4, err := ipAllocator.RequestIP(n.bridge.bridgeIPv4, nil) - if err != nil { - return err - } - ipv4Addr := &net.IPNet{IP: ip4, Mask: n.bridge.bridgeIPv4.Mask} + // Create the sandbox side pipe interface + endpoint.srcName = containerIfName + endpoint.macAddress = ifInfo.MacAddress() + endpoint.addr = ifInfo.Address() + endpoint.addrv6 = ifInfo.AddressIPv6() // Down the interface before configuring mac address. if err = netlink.LinkSetDown(sbox); err != nil { @@ -1013,56 +935,38 @@ func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, } // Set the sbox's MAC. If specified, use the one configured by user, otherwise generate one based on IP. - mac := electMacAddress(epConfig, ip4) - err = netlink.LinkSetHardwareAddr(sbox, mac) + if endpoint.macAddress == nil { + endpoint.macAddress = electMacAddress(epConfig, endpoint.addr.IP) + if err := ifInfo.SetMacAddress(endpoint.macAddress); err != nil { + return err + } + } + err = netlink.LinkSetHardwareAddr(sbox, endpoint.macAddress) if err != nil { return fmt.Errorf("could not set mac address for container interface %s: %v", containerIfName, err) } - endpoint.macAddress = mac // Up the host interface after finishing all netlink configuration if err = netlink.LinkSetUp(host); err != nil { return fmt.Errorf("could not set link up for host interface %s: %v", hostIfName, err) } - // v6 address for the sandbox side pipe interface - ipv6Addr = &net.IPNet{} - if config.EnableIPv6 { + if endpoint.addrv6 == nil && config.EnableIPv6 { var ip6 net.IP - network := n.bridge.bridgeIPv6 - if config.FixedCIDRv6 != nil { - network = config.FixedCIDRv6 - } - ones, _ := network.Mask.Size() if ones <= 80 { ip6 = make(net.IP, len(network.IP)) copy(ip6, network.IP) - for i, h := range mac { + for i, h := range endpoint.macAddress { ip6[i+10] = h } } - ip6, err := ipAllocator.RequestIP(network, ip6) - if err != nil { + endpoint.addrv6 = &net.IPNet{IP: ip6, Mask: network.Mask} + if err := ifInfo.SetIPAddress(endpoint.addrv6); err != nil { return err } - - ipv6Addr = &net.IPNet{IP: ip6, Mask: network.Mask} - } - - // Create the sandbox side pipe interface - endpoint.srcName = containerIfName - endpoint.addr = ipv4Addr - - if config.EnableIPv6 { - endpoint.addrv6 = ipv6Addr - } - - err = epInfo.AddInterface(endpoint.macAddress, *ipv4Addr, *ipv6Addr) - if err != nil { - return err } // Program any required port mapping and store them in the endpoint @@ -1128,28 +1032,6 @@ func (d *driver) DeleteEndpoint(nid, eid string) error { // Remove port mappings. Do not stop endpoint delete on unmap failure n.releasePorts(ep) - // Release the v4 address allocated to this endpoint's sandbox interface - err = ipAllocator.ReleaseIP(n.bridge.bridgeIPv4, ep.addr.IP) - if err != nil { - return err - } - - n.Lock() - config := n.config - n.Unlock() - - // Release the v6 address allocated to this endpoint's sandbox interface - if config.EnableIPv6 { - network := n.bridge.bridgeIPv6 - if config.FixedCIDRv6 != nil { - network = config.FixedCIDRv6 - } - err := ipAllocator.ReleaseIP(network, ep.addrv6.IP) - if err != nil { - return err - } - } - // Try removal of link. Discard error: link pair might have // already been deleted by sandbox delete. Make sure defer // does not see this error either. diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge_store.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge_store.go new file mode 100644 index 0000000000..96c31d87e8 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge_store.go @@ -0,0 +1,211 @@ +package bridge + +import ( + "encoding/json" + "fmt" + "net" + + "github.com/Sirupsen/logrus" + "github.com/docker/libkv/store" + "github.com/docker/libkv/store/boltdb" + "github.com/docker/libnetwork/datastore" + "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/types" +) + +const bridgePrefix = "bridge" + +func (d *driver) initStore(option map[string]interface{}) error { + var err error + + provider, provOk := option[netlabel.LocalKVProvider] + provURL, urlOk := option[netlabel.LocalKVProviderURL] + + if provOk && urlOk { + cfg := &datastore.ScopeCfg{ + Client: datastore.ScopeClientCfg{ + Provider: provider.(string), + Address: provURL.(string), + }, + } + + provConfig, confOk := option[netlabel.LocalKVProviderConfig] + if confOk { + cfg.Client.Config = provConfig.(*store.Config) + } + + d.store, err = datastore.NewDataStore(datastore.LocalScope, cfg) + if err != nil { + return fmt.Errorf("bridge driver failed to initialize data store: %v", err) + } + + return d.populateNetworks() + } + + return nil +} + +func (d *driver) populateNetworks() error { + kvol, err := d.store.List(datastore.Key(bridgePrefix), &networkConfiguration{}) + if err != nil && err != datastore.ErrKeyNotFound && err != boltdb.ErrBoltBucketNotFound { + return fmt.Errorf("failed to get bridge network configurations from store: %v", err) + } + + // It's normal for network configuration state to be empty. Just return. + if err == datastore.ErrKeyNotFound { + return nil + } + + for _, kvo := range kvol { + ncfg := kvo.(*networkConfiguration) + if err = d.createNetwork(ncfg); err != nil { + logrus.Warnf("could not create bridge network for id %s bridge name %s while booting up from persistent state", ncfg.ID, ncfg.BridgeName) + } + } + + return nil +} + +func (d *driver) storeUpdate(kvObject datastore.KVObject) error { + if d.store == nil { + logrus.Warnf("bridge store not initialized. kv object %s is not added to the store", datastore.Key(kvObject.Key()...)) + return nil + } + + if err := d.store.PutObjectAtomic(kvObject); err != nil { + return fmt.Errorf("failed to update bridge store for object type %T: %v", kvObject, err) + } + + return nil +} + +func (d *driver) storeDelete(kvObject datastore.KVObject) error { + if d.store == nil { + logrus.Debugf("bridge store not initialized. kv object %s is not deleted from store", datastore.Key(kvObject.Key()...)) + return nil + } + +retry: + if err := d.store.DeleteObjectAtomic(kvObject); err != nil { + if err == datastore.ErrKeyModified { + if err := d.store.GetObject(datastore.Key(kvObject.Key()...), kvObject); err != nil { + return fmt.Errorf("could not update the kvobject to latest when trying to delete: %v", err) + } + goto retry + } + return err + } + + return nil +} + +func (ncfg *networkConfiguration) MarshalJSON() ([]byte, error) { + nMap := make(map[string]interface{}) + nMap["ID"] = ncfg.ID + nMap["BridgeName"] = ncfg.BridgeName + nMap["EnableIPv6"] = ncfg.EnableIPv6 + nMap["EnableIPMasquerade"] = ncfg.EnableIPMasquerade + nMap["EnableICC"] = ncfg.EnableICC + nMap["Mtu"] = ncfg.Mtu + nMap["DefaultBridge"] = ncfg.DefaultBridge + nMap["DefaultBindingIP"] = ncfg.DefaultBindingIP.String() + nMap["DefaultGatewayIPv4"] = ncfg.DefaultGatewayIPv4.String() + nMap["DefaultGatewayIPv6"] = ncfg.DefaultGatewayIPv6.String() + + if ncfg.AddressIPv4 != nil { + nMap["AddressIPv4"] = ncfg.AddressIPv4.String() + } + + if ncfg.AddressIPv6 != nil { + nMap["AddressIPv6"] = ncfg.AddressIPv6.String() + } + + return json.Marshal(nMap) +} + +func (ncfg *networkConfiguration) UnmarshalJSON(b []byte) error { + var ( + err error + nMap map[string]interface{} + ) + + if err = json.Unmarshal(b, &nMap); err != nil { + return err + } + + if v, ok := nMap["AddressIPv4"]; ok { + if ncfg.AddressIPv4, err = types.ParseCIDR(v.(string)); err != nil { + return types.InternalErrorf("failed to decode bridge network address IPv4 after json unmarshal: %s", v.(string)) + } + } + + if v, ok := nMap["AddressIPv6"]; ok { + if ncfg.AddressIPv6, err = types.ParseCIDR(v.(string)); err != nil { + return types.InternalErrorf("failed to decode bridge network address IPv6 after json unmarshal: %s", v.(string)) + } + } + + ncfg.DefaultBridge = nMap["DefaultBridge"].(bool) + ncfg.DefaultBindingIP = net.ParseIP(nMap["DefaultBindingIP"].(string)) + ncfg.DefaultGatewayIPv4 = net.ParseIP(nMap["DefaultGatewayIPv4"].(string)) + ncfg.DefaultGatewayIPv6 = net.ParseIP(nMap["DefaultGatewayIPv6"].(string)) + ncfg.ID = nMap["ID"].(string) + ncfg.BridgeName = nMap["BridgeName"].(string) + ncfg.EnableIPv6 = nMap["EnableIPv6"].(bool) + ncfg.EnableIPMasquerade = nMap["EnableIPMasquerade"].(bool) + ncfg.EnableICC = nMap["EnableICC"].(bool) + ncfg.Mtu = int(nMap["Mtu"].(float64)) + + return nil +} + +func (ncfg *networkConfiguration) Key() []string { + return []string{bridgePrefix, ncfg.ID} +} + +func (ncfg *networkConfiguration) KeyPrefix() []string { + return []string{bridgePrefix} +} + +func (ncfg *networkConfiguration) Value() []byte { + b, err := json.Marshal(ncfg) + if err != nil { + return nil + } + return b +} + +func (ncfg *networkConfiguration) SetValue(value []byte) error { + return json.Unmarshal(value, ncfg) +} + +func (ncfg *networkConfiguration) Index() uint64 { + return ncfg.dbIndex +} + +func (ncfg *networkConfiguration) SetIndex(index uint64) { + ncfg.dbIndex = index + ncfg.dbExists = true +} + +func (ncfg *networkConfiguration) Exists() bool { + return ncfg.dbExists +} + +func (ncfg *networkConfiguration) Skip() bool { + return ncfg.DefaultBridge +} + +func (ncfg *networkConfiguration) New() datastore.KVObject { + return &networkConfiguration{} +} + +func (ncfg *networkConfiguration) CopyTo(o datastore.KVObject) error { + dstNcfg := o.(*networkConfiguration) + *dstNcfg = *ncfg + return nil +} + +func (ncfg *networkConfiguration) DataScope() string { + return datastore.LocalScope +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/labels.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/labels.go new file mode 100644 index 0000000000..7447bd3f93 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/labels.go @@ -0,0 +1,18 @@ +package bridge + +const ( + // BridgeName label for bridge driver + BridgeName = "com.docker.network.bridge.name" + + // EnableIPMasquerade label for bridge driver + EnableIPMasquerade = "com.docker.network.bridge.enable_ip_masquerade" + + // EnableICC label + EnableICC = "com.docker.network.bridge.enable_icc" + + // DefaultBindingIP label + DefaultBindingIP = "com.docker.network.bridge.host_binding_ipv4" + + // DefaultBridge label + DefaultBridge = "com.docker.network.bridge.default_bridge" +) diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_bridgenetfiltering.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_bridgenetfiltering.go index e7a5f4b077..f7a536adb0 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_bridgenetfiltering.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_bridgenetfiltering.go @@ -22,7 +22,7 @@ const ( //Gets the IP version in use ( [ipv4], [ipv6] or [ipv4 and ipv6] ) func getIPVersion(config *networkConfiguration) ipVersion { ipVersion := ipv4 - if config.FixedCIDRv6 != nil || config.EnableIPv6 { + if config.AddressIPv6 != nil || config.EnableIPv6 { ipVersion |= ipv6 } return ipVersion diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_fixedcidrv4.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_fixedcidrv4.go deleted file mode 100644 index 0702053430..0000000000 --- a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_fixedcidrv4.go +++ /dev/null @@ -1,19 +0,0 @@ -package bridge - -import ( - log "github.com/Sirupsen/logrus" -) - -func setupFixedCIDRv4(config *networkConfiguration, i *bridgeInterface) error { - addrv4, _, err := i.addresses() - if err != nil { - return err - } - - log.Debugf("Using IPv4 subnet: %v", config.FixedCIDR) - if err := ipAllocator.RegisterSubnet(addrv4.IPNet, config.FixedCIDR); err != nil { - return &FixedCIDRv4Error{Subnet: config.FixedCIDR, Net: addrv4.IPNet, Err: err} - } - - return nil -} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_fixedcidrv6.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_fixedcidrv6.go deleted file mode 100644 index b2a949be5b..0000000000 --- a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_fixedcidrv6.go +++ /dev/null @@ -1,27 +0,0 @@ -package bridge - -import ( - "os" - - log "github.com/Sirupsen/logrus" - "github.com/vishvananda/netlink" -) - -func setupFixedCIDRv6(config *networkConfiguration, i *bridgeInterface) error { - log.Debugf("Using IPv6 subnet: %v", config.FixedCIDRv6) - if err := ipAllocator.RegisterSubnet(config.FixedCIDRv6, config.FixedCIDRv6); err != nil { - return &FixedCIDRv6Error{Net: config.FixedCIDRv6, Err: err} - } - - // Setting route to global IPv6 subnet - log.Debugf("Adding route to IPv6 network %s via device %s", config.FixedCIDRv6.String(), config.BridgeName) - err := netlink.RouteAdd(&netlink.Route{ - Scope: netlink.SCOPE_UNIVERSE, - LinkIndex: i.Link.Attrs().Index, - Dst: config.FixedCIDRv6, - }) - if err != nil && !os.IsExist(err) { - log.Errorf("Could not add route to IPv6 network %s via device %s", config.FixedCIDRv6.String(), config.BridgeName) - } - return nil -} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv4.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv4.go index e63c18df21..db913909c1 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv4.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv4.go @@ -3,129 +3,43 @@ package bridge import ( "fmt" "io/ioutil" - "net" "path/filepath" log "github.com/Sirupsen/logrus" - "github.com/docker/libnetwork/netutils" + "github.com/docker/libnetwork/types" "github.com/vishvananda/netlink" ) -var bridgeNetworks []*net.IPNet - -func init() { - // Here we don't follow the convention of using the 1st IP of the range for the gateway. - // This is to use the same gateway IPs as the /24 ranges, which predate the /16 ranges. - // In theory this shouldn't matter - in practice there's bound to be a few scripts relying - // on the internal addressing or other stupid things like that. - // They shouldn't, but hey, let's not break them unless we really have to. - // Don't use 172.16.0.0/16, it conflicts with EC2 DNS 172.16.0.23 - - // 172.[17-31].42.1/16 - mask := []byte{255, 255, 0, 0} - for i := 17; i < 32; i++ { - bridgeNetworks = append(bridgeNetworks, &net.IPNet{IP: []byte{172, byte(i), 42, 1}, Mask: mask}) - } - // 10.[0-255].42.1/16 - for i := 0; i < 256; i++ { - bridgeNetworks = append(bridgeNetworks, &net.IPNet{IP: []byte{10, byte(i), 42, 1}, Mask: mask}) - } - // 192.168.[42-44].1/24 - mask24 := []byte{255, 255, 255, 0} - for i := 42; i < 45; i++ { - bridgeNetworks = append(bridgeNetworks, &net.IPNet{IP: []byte{192, 168, byte(i), 1}, Mask: mask24}) - } -} - func setupBridgeIPv4(config *networkConfiguration, i *bridgeInterface) error { addrv4, _, err := i.addresses() if err != nil { - return err + return fmt.Errorf("failed to retrieve bridge interface addresses: %v", err) } - // Check if we have an IP address already on the bridge. - if addrv4.IPNet != nil { - // Make sure to store bridge network and default gateway before getting out. - i.bridgeIPv4 = addrv4.IPNet - i.gatewayIPv4 = addrv4.IPNet.IP - return nil - } - - // Do not try to configure IPv4 on a non-default bridge unless you are - // specifically asked to do so. - if config.BridgeName != DefaultBridgeName && config.DefaultBridge { - return NonDefaultBridgeNeedsIPError(config.BridgeName) - } - - bridgeIPv4, err := electBridgeIPv4(config) - if err != nil { - return err - } - - log.Debugf("Creating bridge interface %s with network %s", config.BridgeName, bridgeIPv4) - if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: bridgeIPv4}); err != nil { - return &IPv4AddrAddError{IP: bridgeIPv4, Err: err} + if !types.CompareIPNet(addrv4.IPNet, config.AddressIPv4) { + if addrv4.IPNet != nil { + if err := netlink.AddrDel(i.Link, &addrv4); err != nil { + return fmt.Errorf("failed to remove current ip address from bridge: %v", err) + } + } + log.Debugf("Assigning address to bridge interface %s: %s", config.BridgeName, config.AddressIPv4) + if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: config.AddressIPv4}); err != nil { + return &IPv4AddrAddError{IP: config.AddressIPv4, Err: err} + } } // Store bridge network and default gateway - i.bridgeIPv4 = bridgeIPv4 - i.gatewayIPv4 = i.bridgeIPv4.IP + i.bridgeIPv4 = config.AddressIPv4 + i.gatewayIPv4 = config.AddressIPv4.IP return nil } -func allocateBridgeIP(config *networkConfiguration, i *bridgeInterface) error { - // Because of the way ipallocator manages the container address space, - // reserve bridge address only if it belongs to the container network - // (if defined), no need otherwise - if config.FixedCIDR == nil || config.FixedCIDR.Contains(i.bridgeIPv4.IP) { - if _, err := ipAllocator.RequestIP(i.bridgeIPv4, i.bridgeIPv4.IP); err != nil { - return fmt.Errorf("failed to reserve bridge IP %s: %v", i.bridgeIPv4.IP.String(), err) - } - } - return nil -} - -func electBridgeIPv4(config *networkConfiguration) (*net.IPNet, error) { - // Use the requested IPv4 CIDR when available. - if config.AddressIPv4 != nil { - return config.AddressIPv4, nil - } - - // We don't check for an error here, because we don't really care if we - // can't read /etc/resolv.conf. So instead we skip the append if resolvConf - // is nil. It either doesn't exist, or we can't read it for some reason. - nameservers := []string{} - if resolvConf, _ := readResolvConf(); resolvConf != nil { - nameservers = append(nameservers, getNameserversAsCIDR(resolvConf)...) - } - - // Try to automatically elect appropriate bridge IPv4 settings. - for _, n := range bridgeNetworks { - if err := netutils.CheckNameserverOverlaps(nameservers, n); err == nil { - if err := netutils.CheckRouteOverlaps(n); err == nil { - return n, nil - } - } - } - - return nil, IPv4AddrRangeError(config.BridgeName) -} - func setupGatewayIPv4(config *networkConfiguration, i *bridgeInterface) error { if !i.bridgeIPv4.Contains(config.DefaultGatewayIPv4) { return &ErrInvalidGateway{} } - // Because of the way ipallocator manages the container address space, - // reserve default gw address only if it belongs to the container network - // (if defined), no need otherwise - if config.FixedCIDR == nil || config.FixedCIDR.Contains(config.DefaultGatewayIPv4) { - if _, err := ipAllocator.RequestIP(i.bridgeIPv4, config.DefaultGatewayIPv4); err != nil { - return fmt.Errorf("failed to reserve default gateway %s: %v", config.DefaultGatewayIPv4.String(), err) - } - } - // Store requested default gateway i.gatewayIPv4 = config.DefaultGatewayIPv4 diff --git a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv6.go b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv6.go index da3c9db9c3..5ce53dd879 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv6.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/bridge/setup_ipv6.go @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" "net" + "os" "github.com/Sirupsen/logrus" "github.com/vishvananda/netlink" @@ -57,19 +58,31 @@ func setupBridgeIPv6(config *networkConfiguration, i *bridgeInterface) error { i.bridgeIPv6 = bridgeIPv6 i.gatewayIPv6 = i.bridgeIPv6.IP + if config.AddressIPv6 == nil { + return nil + } + + // Setting route to global IPv6 subnet + logrus.Debugf("Adding route to IPv6 network %s via device %s", config.AddressIPv6.String(), config.BridgeName) + err = netlink.RouteAdd(&netlink.Route{ + Scope: netlink.SCOPE_UNIVERSE, + LinkIndex: i.Link.Attrs().Index, + Dst: config.AddressIPv6, + }) + if err != nil && !os.IsExist(err) { + logrus.Errorf("Could not add route to IPv6 network %s via device %s", config.AddressIPv6.String(), config.BridgeName) + } + return nil } func setupGatewayIPv6(config *networkConfiguration, i *bridgeInterface) error { - if config.FixedCIDRv6 == nil { + if config.AddressIPv6 == nil { return &ErrInvalidContainerSubnet{} } - if !config.FixedCIDRv6.Contains(config.DefaultGatewayIPv6) { + if !config.AddressIPv6.Contains(config.DefaultGatewayIPv6) { return &ErrInvalidGateway{} } - if _, err := ipAllocator.RequestIP(config.FixedCIDRv6, config.DefaultGatewayIPv6); err != nil { - return err - } // Store requested default gateway i.gatewayIPv6 = config.DefaultGatewayIPv6 diff --git a/vendor/src/github.com/docker/libnetwork/drivers/host/host.go b/vendor/src/github.com/docker/libnetwork/drivers/host/host.go index 2549ed866e..340cb2e6f0 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/host/host.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/host/host.go @@ -23,7 +23,7 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error { return dc.RegisterDriver(networkType, &driver{}, c) } -func (d *driver) CreateNetwork(id string, option map[string]interface{}) error { +func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error { d.Lock() defer d.Unlock() @@ -40,7 +40,7 @@ func (d *driver) DeleteNetwork(nid string) error { return types.ForbiddenErrorf("network of type \"%s\" cannot be deleted", networkType) } -func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { +func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error { return nil } diff --git a/vendor/src/github.com/docker/libnetwork/drivers/null/null.go b/vendor/src/github.com/docker/libnetwork/drivers/null/null.go index 670fc68672..5fbdd12956 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/null/null.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/null/null.go @@ -23,7 +23,7 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error { return dc.RegisterDriver(networkType, &driver{}, c) } -func (d *driver) CreateNetwork(id string, option map[string]interface{}) error { +func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error { d.Lock() defer d.Unlock() @@ -40,7 +40,7 @@ func (d *driver) DeleteNetwork(nid string) error { return types.ForbiddenErrorf("network of type \"%s\" cannot be deleted", networkType) } -func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { +func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error { return nil } diff --git a/vendor/src/github.com/docker/libnetwork/drivers/overlay/joinleave.go b/vendor/src/github.com/docker/libnetwork/drivers/overlay/joinleave.go index 9fa73f1c33..1ce123cc51 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/overlay/joinleave.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/overlay/joinleave.go @@ -4,7 +4,9 @@ import ( "fmt" "net" + log "github.com/Sirupsen/logrus" "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/types" "github.com/vishvananda/netlink" ) @@ -24,11 +26,27 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, return fmt.Errorf("could not find endpoint with id %s", eid) } - if err := n.joinSandbox(); err != nil { - return fmt.Errorf("network sandbox join failed: %v", - err) + s := n.getSubnetforIP(ep.addr) + if s == nil { + return fmt.Errorf("could not find subnet for endpoint %s", eid) } + if err := n.obtainVxlanID(s); err != nil { + return fmt.Errorf("couldn't get vxlan id for %q: %v", s.subnetIP.String(), err) + } + + if err := n.joinSandbox(); err != nil { + return fmt.Errorf("network sandbox join failed: %v", err) + } + + if err := n.joinSubnetSandbox(s); err != nil { + return fmt.Errorf("subnet sandbox join failed for %q: %v", s.subnetIP.String(), err) + } + + // joinSubnetSandbox gets called when an endpoint comes up on a new subnet in the + // overlay network. Hence the Endpoint count should be updated outside joinSubnetSandbox + n.incEndpointCount() + sbox := n.sandbox() name1, name2, err := createVethPair() @@ -49,7 +67,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, } if err := sbox.AddInterface(name1, "veth", - sbox.InterfaceOptions().Master("bridge1")); err != nil { + sbox.InterfaceOptions().Master(s.brName)); err != nil { return fmt.Errorf("could not add veth pair inside the network sandbox: %v", err) } @@ -63,7 +81,16 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, } if err := netlink.LinkSetHardwareAddr(veth, ep.mac); err != nil { - return fmt.Errorf("could not set mac address to the container interface: %v", err) + return fmt.Errorf("could not set mac address (%v) to the container interface: %v", ep.mac, err) + } + + for _, sub := range n.subnets { + if sub == s { + continue + } + if err := jinfo.AddStaticRoute(sub.subnetIP, types.NEXTHOP, s.gwIP.IP); err != nil { + log.Errorf("Adding subnet %s static route in network %q failed\n", s.subnetIP, n.id) + } } if iNames := jinfo.InterfaceName(); iNames != nil { @@ -73,7 +100,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, } } - d.peerDbAdd(nid, eid, ep.addr.IP, ep.mac, + d.peerDbAdd(nid, eid, ep.addr.IP, ep.addr.Mask, ep.mac, net.ParseIP(d.bindAddress), true) d.pushLocalEndpointEvent("join", nid, eid) diff --git a/vendor/src/github.com/docker/libnetwork/drivers/overlay/ov_endpoint.go b/vendor/src/github.com/docker/libnetwork/drivers/overlay/ov_endpoint.go index 71e73c4e0a..7a861a97a3 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/overlay/ov_endpoint.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/overlay/ov_endpoint.go @@ -1,7 +1,6 @@ package overlay import ( - "encoding/binary" "fmt" "net" @@ -36,9 +35,18 @@ func (n *network) deleteEndpoint(eid string) { n.Unlock() } -func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, +func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error { - if err := validateID(nid, eid); err != nil { + var err error + + if err = validateID(nid, eid); err != nil { + return err + } + + // Since we perform lazy configuration make sure we try + // configuring the driver when we enter CreateEndpoint since + // CreateNetwork may not be called in every node. + if err := d.configure(); err != nil { return err } @@ -48,35 +56,23 @@ func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, } ep := &endpoint{ - id: eid, + id: eid, + addr: ifInfo.Address(), + mac: ifInfo.MacAddress(), + } + if ep.addr == nil { + return fmt.Errorf("create endpoint was not passed interface IP address") } - if epInfo != nil && epInfo.Interface() != nil { - addr := epInfo.Interface().Address() - ep.addr = &addr - ep.mac = epInfo.Interface().MacAddress() - n.addEndpoint(ep) - return nil + if s := n.getSubnetforIP(ep.addr); s == nil { + return fmt.Errorf("no matching subnet for IP %q in network %q\n", ep.addr, nid) } - ipID, err := d.ipAllocator.GetID() - if err != nil { - return fmt.Errorf("could not allocate ip from subnet %s: %v", - bridgeSubnet.String(), err) - } - - ep.addr = &net.IPNet{ - Mask: bridgeSubnet.Mask, - } - ep.addr.IP = make([]byte, 4) - - binary.BigEndian.PutUint32(ep.addr.IP, bridgeSubnetInt+ipID) - - ep.mac = netutils.GenerateMACFromIP(ep.addr.IP) - - err = epInfo.AddInterface(ep.mac, *ep.addr, net.IPNet{}) - if err != nil { - return fmt.Errorf("could not add interface to endpoint info: %v", err) + if ep.mac == nil { + ep.mac = netutils.GenerateMACFromIP(ep.addr.IP) + if err := ifInfo.SetMacAddress(ep.mac); err != nil { + return err + } } n.addEndpoint(ep) @@ -99,7 +95,6 @@ func (d *driver) DeleteEndpoint(nid, eid string) error { return fmt.Errorf("endpoint id %q not found", eid) } - d.ipAllocator.Release(binary.BigEndian.Uint32(ep.addr.IP) - bridgeSubnetInt) n.deleteEndpoint(eid) return nil } diff --git a/vendor/src/github.com/docker/libnetwork/drivers/overlay/ov_network.go b/vendor/src/github.com/docker/libnetwork/drivers/overlay/ov_network.go index 9e5f9d8909..d09fd56975 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/overlay/ov_network.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/overlay/ov_network.go @@ -9,37 +9,54 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/libnetwork/datastore" - "github.com/docker/libnetwork/ipallocator" + "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/netutils" "github.com/docker/libnetwork/osl" + "github.com/docker/libnetwork/types" "github.com/vishvananda/netlink" "github.com/vishvananda/netlink/nl" ) type networkTable map[string]*network +type subnet struct { + once *sync.Once + vxlanName string + brName string + vni uint32 + initErr error + subnetIP *net.IPNet + gwIP *net.IPNet +} + +type subnetJSON struct { + SubnetIP string + GwIP string + Vni uint32 +} + type network struct { - id string - vni uint32 - dbIndex uint64 - dbExists bool - sbox osl.Sandbox - endpoints endpointTable - ipAllocator *ipallocator.IPAllocator - gw net.IP - vxlanName string - driver *driver - joinCnt int - once *sync.Once - initEpoch int - initErr error + id string + dbIndex uint64 + dbExists bool + sbox osl.Sandbox + endpoints endpointTable + driver *driver + joinCnt int + once *sync.Once + initEpoch int + initErr error + subnets []*subnet sync.Mutex } -func (d *driver) CreateNetwork(id string, option map[string]interface{}) error { +func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error { if id == "" { return fmt.Errorf("invalid network id") } + // Since we perform lazy configuration make sure we try + // configuring the driver when we enter CreateNetwork if err := d.configure(); err != nil { return err } @@ -49,19 +66,43 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}) error { driver: d, endpoints: endpointTable{}, once: &sync.Once{}, + subnets: []*subnet{}, } - n.gw = bridgeIP.IP + for _, ipd := range ipV4Data { + s := &subnet{ + subnetIP: ipd.Pool, + gwIP: ipd.Gateway, + once: &sync.Once{}, + } + n.subnets = append(n.subnets, s) + } + + if err := n.writeToStore(); err != nil { + return fmt.Errorf("failed to update data store for network %v: %v", n.id, err) + } d.addNetwork(n) - if err := n.obtainVxlanID(); err != nil { - return err - } - return nil } +/* func (d *driver) createNetworkfromStore(nid string) (*network, error) { + n := &network{ + id: nid, + driver: d, + endpoints: endpointTable{}, + once: &sync.Once{}, + subnets: []*subnet{}, + } + + err := d.store.GetObject(datastore.Key(n.Key()...), n) + if err != nil { + return nil, fmt.Errorf("unable to get network %q from data store, %v", nid, err) + } + return n, nil +}*/ + func (d *driver) DeleteNetwork(nid string) error { if nid == "" { return fmt.Errorf("invalid network id") @@ -77,15 +118,13 @@ func (d *driver) DeleteNetwork(nid string) error { return n.releaseVxlanID() } -func (n *network) joinSandbox() error { +func (n *network) incEndpointCount() { n.Lock() - if n.joinCnt != 0 { - n.joinCnt++ - n.Unlock() - return nil - } - n.Unlock() + defer n.Unlock() + n.joinCnt++ +} +func (n *network) joinSandbox() error { // If there is a race between two go routines here only one will win // the other will wait. n.once.Do(func() { @@ -94,16 +133,14 @@ func (n *network) joinSandbox() error { n.initErr = n.initSandbox() }) - // Increment joinCnt in all the goroutines only when the one time initSandbox - // was a success. - n.Lock() - if n.initErr == nil { - n.joinCnt++ - } - err := n.initErr - n.Unlock() + return n.initErr +} - return err +func (n *network) joinSubnetSandbox(s *subnet) error { + s.once.Do(func() { + s.initErr = n.initSubnetSandbox(s) + }) + return s.initErr } func (n *network) leaveSandbox() { @@ -118,6 +155,9 @@ func (n *network) leaveSandbox() { // Reinitialize the once variable so that we will be able to trigger one time // sandbox initialization(again) when another container joins subsequently. n.once = &sync.Once{} + for _, s := range n.subnets { + s.once = &sync.Once{} + } n.Unlock() n.destroySandbox() @@ -130,14 +170,50 @@ func (n *network) destroySandbox() { iface.Remove() } - if err := deleteVxlan(n.vxlanName); err != nil { - logrus.Warnf("could not cleanup sandbox properly: %v", err) + for _, s := range n.subnets { + if s.vxlanName != "" { + err := deleteVxlan(s.vxlanName) + if err != nil { + logrus.Warnf("could not cleanup sandbox properly: %v", err) + } + } } - sbox.Destroy() } } +func (n *network) initSubnetSandbox(s *subnet) error { + // create a bridge and vxlan device for this subnet and move it to the sandbox + brName, err := netutils.GenerateIfaceName("bridge", 7) + if err != nil { + return err + } + sbox := n.sandbox() + + if err := sbox.AddInterface(brName, "br", + sbox.InterfaceOptions().Address(s.gwIP), + sbox.InterfaceOptions().Bridge(true)); err != nil { + return fmt.Errorf("bridge creation in sandbox failed for subnet %q: %v", s.subnetIP.IP.String(), err) + } + + vxlanName, err := createVxlan(n.vxlanID(s)) + if err != nil { + return err + } + + if err := sbox.AddInterface(vxlanName, "vxlan", + sbox.InterfaceOptions().Master(brName)); err != nil { + return fmt.Errorf("vxlan interface creation failed for subnet %q: %v", s.subnetIP.IP.String(), err) + } + + n.Lock() + s.vxlanName = vxlanName + s.brName = brName + n.Unlock() + + return nil +} + func (n *network) initSandbox() error { n.Lock() n.initEpoch++ @@ -149,15 +225,10 @@ func (n *network) initSandbox() error { return fmt.Errorf("could not create network sandbox: %v", err) } - // Add a bridge inside the namespace - if err := sbox.AddInterface("bridge1", "br", - sbox.InterfaceOptions().Address(bridgeIP), - sbox.InterfaceOptions().Bridge(true)); err != nil { - return fmt.Errorf("could not create bridge inside the network sandbox: %v", err) - } - n.setSandbox(sbox) + n.driver.peerDbUpdateSandbox(n.id) + var nlSock *nl.NetlinkSocket sbox.InvokeFunc(func() { nlSock, err = nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_NEIGH) @@ -167,27 +238,6 @@ func (n *network) initSandbox() error { }) go n.watchMiss(nlSock) - return n.initVxlan() -} - -func (n *network) initVxlan() error { - var vxlanName string - n.Lock() - sbox := n.sbox - n.Unlock() - - vxlanName, err := createVxlan(n.vxlanID()) - if err != nil { - return err - } - - if err = sbox.AddInterface(vxlanName, "vxlan", - sbox.InterfaceOptions().Master("bridge1")); err != nil { - return fmt.Errorf("could not add vxlan interface inside the network sandbox: %v", err) - } - - n.vxlanName = vxlanName - n.driver.peerDbUpdateSandbox(n.id) return nil } @@ -218,14 +268,14 @@ func (n *network) watchMiss(nlSock *nl.NetlinkSocket) { continue } - mac, vtep, err := n.driver.resolvePeer(n.id, neigh.IP) + mac, IPmask, vtep, err := n.driver.resolvePeer(n.id, neigh.IP) if err != nil { logrus.Errorf("could not resolve peer %q: %v", neigh.IP, err) continue } - if err := n.driver.peerAdd(n.id, "dummy", neigh.IP, mac, vtep, true); err != nil { - logrus.Errorf("could not add neighbor entry for missed peer: %v", err) + if err := n.driver.peerAdd(n.id, "dummy", neigh.IP, IPmask, mac, vtep, true); err != nil { + logrus.Errorf("could not add neighbor entry for missed peer %q: %v", neigh.IP, err) } } } @@ -245,9 +295,34 @@ func (d *driver) deleteNetwork(nid string) { func (d *driver) network(nid string) *network { d.Lock() - defer d.Unlock() + networks := d.networks + d.Unlock() - return d.networks[nid] + n, ok := networks[nid] + if !ok { + n = d.getNetworkFromStore(nid) + if n != nil { + n.driver = d + n.endpoints = endpointTable{} + n.once = &sync.Once{} + networks[nid] = n + } + } + + return n +} + +func (d *driver) getNetworkFromStore(nid string) *network { + if d.store == nil { + return nil + } + + n := &network{id: nid} + if err := d.store.GetObject(datastore.Key(n.Key()...), n); err != nil { + return nil + } + + return n } func (n *network) sandbox() osl.Sandbox { @@ -263,16 +338,16 @@ func (n *network) setSandbox(sbox osl.Sandbox) { n.Unlock() } -func (n *network) vxlanID() uint32 { +func (n *network) vxlanID(s *subnet) uint32 { n.Lock() defer n.Unlock() - return n.vni + return s.vni } -func (n *network) setVxlanID(vni uint32) { +func (n *network) setVxlanID(s *subnet, vni uint32) { n.Lock() - n.vni = vni + s.vni = vni n.Unlock() } @@ -285,11 +360,22 @@ func (n *network) KeyPrefix() []string { } func (n *network) Value() []byte { - b, err := json.Marshal(n.vxlanID()) + netJSON := []*subnetJSON{} + + for _, s := range n.subnets { + sj := &subnetJSON{ + SubnetIP: s.subnetIP.String(), + GwIP: s.gwIP.String(), + Vni: s.vni, + } + netJSON = append(netJSON, sj) + } + + b, err := json.Marshal(netJSON) + if err != nil { return []byte{} } - return b } @@ -311,15 +397,45 @@ func (n *network) Skip() bool { } func (n *network) SetValue(value []byte) error { - var vni uint32 - err := json.Unmarshal(value, &vni) - if err == nil { - n.setVxlanID(vni) + var newNet bool + netJSON := []*subnetJSON{} + + err := json.Unmarshal(value, &netJSON) + if err != nil { + return err } - return err + + if len(n.subnets) == 0 { + newNet = true + } + + for _, sj := range netJSON { + subnetIPstr := sj.SubnetIP + gwIPstr := sj.GwIP + vni := sj.Vni + + subnetIP, _ := types.ParseCIDR(subnetIPstr) + gwIP, _ := types.ParseCIDR(gwIPstr) + + if newNet { + s := &subnet{ + subnetIP: subnetIP, + gwIP: gwIP, + vni: vni, + once: &sync.Once{}, + } + n.subnets = append(n.subnets, s) + } else { + sNet := n.getMatchingSubnet(subnetIP) + if sNet != nil { + sNet.vni = vni + } + } + } + return nil } -func (n *network) DataScope() datastore.DataScope { +func (n *network) DataScope() string { return datastore.GlobalScope } @@ -332,7 +448,7 @@ func (n *network) releaseVxlanID() error { return fmt.Errorf("no datastore configured. cannot release vxlan id") } - if n.vxlanID() == 0 { + if len(n.subnets) == 0 { return nil } @@ -346,38 +462,80 @@ func (n *network) releaseVxlanID() error { return fmt.Errorf("failed to delete network to vxlan id map: %v", err) } - n.driver.vxlanIdm.Release(n.vxlanID()) - n.setVxlanID(0) + for _, s := range n.subnets { + n.driver.vxlanIdm.Release(uint64(n.vxlanID(s))) + n.setVxlanID(s, 0) + } return nil } -func (n *network) obtainVxlanID() error { +func (n *network) obtainVxlanID(s *subnet) error { + //return if the subnet already has a vxlan id assigned + if s.vni != 0 { + return nil + } + if n.driver.store == nil { return fmt.Errorf("no datastore configured. cannot obtain vxlan id") } for { - var vxlanID uint32 if err := n.driver.store.GetObject(datastore.Key(n.Key()...), n); err != nil { - if err == datastore.ErrKeyNotFound { - vxlanID, err = n.driver.vxlanIdm.GetID() - if err != nil { - return fmt.Errorf("failed to allocate vxlan id: %v", err) - } + return fmt.Errorf("getting network %q from datastore failed %v", n.id, err) + } - n.setVxlanID(vxlanID) - if err := n.writeToStore(); err != nil { - n.driver.vxlanIdm.Release(n.vxlanID()) - n.setVxlanID(0) - if err == datastore.ErrKeyModified { - continue - } - return fmt.Errorf("failed to update data store with vxlan id: %v", err) - } - return nil + if s.vni == 0 { + vxlanID, err := n.driver.vxlanIdm.GetID() + if err != nil { + return fmt.Errorf("failed to allocate vxlan id: %v", err) } - return fmt.Errorf("failed to obtain vxlan id from data store: %v", err) + + n.setVxlanID(s, uint32(vxlanID)) + if err := n.writeToStore(); err != nil { + n.driver.vxlanIdm.Release(uint64(n.vxlanID(s))) + n.setVxlanID(s, 0) + if err == datastore.ErrKeyModified { + continue + } + return fmt.Errorf("network %q failed to update data store: %v", n.id, err) + } + return nil } return nil } } + +// getSubnetforIP returns the subnet to which the given IP belongs +func (n *network) getSubnetforIP(ip *net.IPNet) *subnet { + for _, s := range n.subnets { + // first check if the mask lengths are the same + i, _ := s.subnetIP.Mask.Size() + j, _ := ip.Mask.Size() + if i != j { + continue + } + if s.subnetIP.Contains(ip.IP) { + return s + } + } + return nil +} + +// getMatchingSubnet return the network's subnet that matches the input +func (n *network) getMatchingSubnet(ip *net.IPNet) *subnet { + if ip == nil { + return nil + } + for _, s := range n.subnets { + // first check if the mask lengths are the same + i, _ := s.subnetIP.Mask.Size() + j, _ := ip.Mask.Size() + if i != j { + continue + } + if s.subnetIP.IP.Equal(ip.IP) { + return s + } + } + return nil +} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/overlay/ov_serf.go b/vendor/src/github.com/docker/libnetwork/drivers/overlay/ov_serf.go index d717c100a6..894cb582ca 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/overlay/ov_serf.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/overlay/ov_serf.go @@ -47,7 +47,7 @@ func (d *driver) serfInit() error { config.UserCoalescePeriod = 1 * time.Second config.UserQuiescentPeriod = 50 * time.Millisecond - config.LogOutput = logrus.StandardLogger().Out + config.LogOutput = &logWriter{} s, err := serf.Create(config) if err != nil { @@ -83,34 +83,35 @@ func (d *driver) notifyEvent(event ovNotify) { n := d.network(event.nid) ep := n.endpoint(event.eid) - ePayload := fmt.Sprintf("%s %s %s", event.action, ep.addr.IP.String(), ep.mac.String()) + ePayload := fmt.Sprintf("%s %s %s %s", event.action, ep.addr.IP.String(), + net.IP(ep.addr.Mask).String(), ep.mac.String()) eName := fmt.Sprintf("jl %s %s %s", d.serfInstance.LocalMember().Addr.String(), event.nid, event.eid) if err := d.serfInstance.UserEvent(eName, []byte(ePayload), true); err != nil { - fmt.Printf("Sending user event failed: %v\n", err) + logrus.Errorf("Sending user event failed: %v\n", err) } } func (d *driver) processEvent(u serf.UserEvent) { - fmt.Printf("Received user event name:%s, payload:%s\n", u.Name, + logrus.Debugf("Received user event name:%s, payload:%s\n", u.Name, string(u.Payload)) - var dummy, action, vtepStr, nid, eid, ipStr, macStr string + var dummy, action, vtepStr, nid, eid, ipStr, maskStr, macStr string if _, err := fmt.Sscan(u.Name, &dummy, &vtepStr, &nid, &eid); err != nil { fmt.Printf("Failed to scan name string: %v\n", err) } if _, err := fmt.Sscan(string(u.Payload), &action, - &ipStr, &macStr); err != nil { + &ipStr, &maskStr, &macStr); err != nil { fmt.Printf("Failed to scan value string: %v\n", err) } - fmt.Printf("Parsed data = %s/%s/%s/%s/%s\n", nid, eid, vtepStr, ipStr, macStr) + logrus.Debugf("Parsed data = %s/%s/%s/%s/%s/%s\n", nid, eid, vtepStr, ipStr, maskStr, macStr) mac, err := net.ParseMAC(macStr) if err != nil { - fmt.Printf("Failed to parse mac: %v\n", err) + logrus.Errorf("Failed to parse mac: %v\n", err) } if d.serfInstance.LocalMember().Addr.String() == vtepStr { @@ -119,20 +120,20 @@ func (d *driver) processEvent(u serf.UserEvent) { switch action { case "join": - if err := d.peerAdd(nid, eid, net.ParseIP(ipStr), mac, + if err := d.peerAdd(nid, eid, net.ParseIP(ipStr), net.IPMask(net.ParseIP(maskStr).To4()), mac, net.ParseIP(vtepStr), true); err != nil { - fmt.Printf("Peer add failed in the driver: %v\n", err) + logrus.Errorf("Peer add failed in the driver: %v\n", err) } case "leave": - if err := d.peerDelete(nid, eid, net.ParseIP(ipStr), mac, + if err := d.peerDelete(nid, eid, net.ParseIP(ipStr), net.IPMask(net.ParseIP(maskStr).To4()), mac, net.ParseIP(vtepStr), true); err != nil { - fmt.Printf("Peer delete failed in the driver: %v\n", err) + logrus.Errorf("Peer delete failed in the driver: %v\n", err) } } } func (d *driver) processQuery(q *serf.Query) { - fmt.Printf("Received query name:%s, payload:%s\n", q.Name, + logrus.Debugf("Received query name:%s, payload:%s\n", q.Name, string(q.Payload)) var nid, ipStr string @@ -140,38 +141,38 @@ func (d *driver) processQuery(q *serf.Query) { fmt.Printf("Failed to scan query payload string: %v\n", err) } - peerMac, vtep, err := d.peerDbSearch(nid, net.ParseIP(ipStr)) + peerMac, peerIPMask, vtep, err := d.peerDbSearch(nid, net.ParseIP(ipStr)) if err != nil { return } - q.Respond([]byte(fmt.Sprintf("%s %s", peerMac.String(), vtep.String()))) + q.Respond([]byte(fmt.Sprintf("%s %s %s", peerMac.String(), net.IP(peerIPMask).String(), vtep.String()))) } -func (d *driver) resolvePeer(nid string, peerIP net.IP) (net.HardwareAddr, net.IP, error) { +func (d *driver) resolvePeer(nid string, peerIP net.IP) (net.HardwareAddr, net.IPMask, net.IP, error) { qPayload := fmt.Sprintf("%s %s", string(nid), peerIP.String()) resp, err := d.serfInstance.Query("peerlookup", []byte(qPayload), nil) if err != nil { - return nil, nil, fmt.Errorf("resolving peer by querying the cluster failed: %v", err) + return nil, nil, nil, fmt.Errorf("resolving peer by querying the cluster failed: %v", err) } respCh := resp.ResponseCh() select { case r := <-respCh: - var macStr, vtepStr string - if _, err := fmt.Sscan(string(r.Payload), &macStr, &vtepStr); err != nil { - return nil, nil, fmt.Errorf("bad response %q for the resolve query: %v", string(r.Payload), err) + var macStr, maskStr, vtepStr string + if _, err := fmt.Sscan(string(r.Payload), &macStr, &maskStr, &vtepStr); err != nil { + return nil, nil, nil, fmt.Errorf("bad response %q for the resolve query: %v", string(r.Payload), err) } mac, err := net.ParseMAC(macStr) if err != nil { - return nil, nil, fmt.Errorf("failed to parse mac: %v", err) + return nil, nil, nil, fmt.Errorf("failed to parse mac: %v", err) } - return mac, net.ParseIP(vtepStr), nil + return mac, net.IPMask(net.ParseIP(maskStr).To4()), net.ParseIP(vtepStr), nil case <-time.After(time.Second): - return nil, nil, fmt.Errorf("timed out resolving peer by querying the cluster") + return nil, nil, nil, fmt.Errorf("timed out resolving peer by querying the cluster") } } @@ -192,7 +193,7 @@ func (d *driver) startSerfLoop(eventCh chan serf.Event, notifyCh chan ovNotify, } if err := d.serfInstance.Leave(); err != nil { - fmt.Printf("failed leaving the cluster: %v\n", err) + logrus.Errorf("failed leaving the cluster: %v\n", err) } d.serfInstance.Shutdown() diff --git a/vendor/src/github.com/docker/libnetwork/drivers/overlay/overlay.go b/vendor/src/github.com/docker/libnetwork/drivers/overlay/overlay.go index 4a8a6e0137..c190d3986a 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/overlay/overlay.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/overlay/overlay.go @@ -1,14 +1,11 @@ package overlay import ( - "encoding/binary" "fmt" - "net" "sync" "github.com/Sirupsen/logrus" "github.com/docker/libkv/store" - "github.com/docker/libnetwork/config" "github.com/docker/libnetwork/datastore" "github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/idm" @@ -44,36 +41,8 @@ type driver struct { sync.Mutex } -var ( - bridgeSubnet, bridgeIP *net.IPNet - once sync.Once - bridgeSubnetInt uint32 -) - -func onceInit() { - var err error - _, bridgeSubnet, err = net.ParseCIDR("172.21.0.0/16") - if err != nil { - panic("could not parse cid 172.21.0.0/16") - } - - bridgeSubnetInt = binary.BigEndian.Uint32(bridgeSubnet.IP.To4()) - - ip, subnet, err := net.ParseCIDR("172.21.255.254/16") - if err != nil { - panic("could not parse cid 172.21.255.254/16") - } - - bridgeIP = &net.IPNet{ - IP: ip, - Mask: subnet.Mask, - } -} - // Init registers a new instance of overlay driver func Init(dc driverapi.DriverCallback, config map[string]interface{}) error { - once.Do(onceInit) - c := driverapi.Capability{ DataScope: datastore.GlobalScope, } @@ -110,21 +79,21 @@ func (d *driver) configure() error { } d.once.Do(func() { - provider, provOk := d.config[netlabel.KVProvider] - provURL, urlOk := d.config[netlabel.KVProviderURL] + provider, provOk := d.config[netlabel.GlobalKVProvider] + provURL, urlOk := d.config[netlabel.GlobalKVProviderURL] if provOk && urlOk { - cfg := &config.DatastoreCfg{ - Client: config.DatastoreClientCfg{ + cfg := &datastore.ScopeCfg{ + Client: datastore.ScopeClientCfg{ Provider: provider.(string), Address: provURL.(string), }, } - provConfig, confOk := d.config[netlabel.KVProviderConfig] + provConfig, confOk := d.config[netlabel.GlobalKVProviderConfig] if confOk { cfg.Client.Config = provConfig.(*store.Config) } - d.store, err = datastore.NewDataStore(cfg) + d.store, err = datastore.NewDataStore(datastore.GlobalScope, cfg) if err != nil { err = fmt.Errorf("failed to initialize data store: %v", err) return diff --git a/vendor/src/github.com/docker/libnetwork/drivers/overlay/peerdb.go b/vendor/src/github.com/docker/libnetwork/drivers/overlay/peerdb.go index 7a56009ff7..22a677d8ba 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/overlay/peerdb.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/overlay/peerdb.go @@ -13,10 +13,11 @@ type peerKey struct { } type peerEntry struct { - eid string - vtep net.IP - inSandbox bool - isLocal bool + eid string + vtep net.IP + peerIPMask net.IPMask + inSandbox bool + isLocal bool } type peerMap struct { @@ -98,16 +99,18 @@ func (d *driver) peerDbNetworkWalk(nid string, f func(*peerKey, *peerEntry) bool return nil } -func (d *driver) peerDbSearch(nid string, peerIP net.IP) (net.HardwareAddr, net.IP, error) { +func (d *driver) peerDbSearch(nid string, peerIP net.IP) (net.HardwareAddr, net.IPMask, net.IP, error) { var ( - peerMac net.HardwareAddr - vtep net.IP - found bool + peerMac net.HardwareAddr + vtep net.IP + peerIPMask net.IPMask + found bool ) err := d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool { if pKey.peerIP.Equal(peerIP) { peerMac = pKey.peerMac + peerIPMask = pEntry.peerIPMask vtep = pEntry.vtep found = true return found @@ -117,17 +120,17 @@ func (d *driver) peerDbSearch(nid string, peerIP net.IP) (net.HardwareAddr, net. }) if err != nil { - return nil, nil, fmt.Errorf("peerdb search for peer ip %q failed: %v", peerIP, err) + return nil, nil, nil, fmt.Errorf("peerdb search for peer ip %q failed: %v", peerIP, err) } if !found { - return nil, nil, fmt.Errorf("peer ip %q not found in peerdb", peerIP) + return nil, nil, nil, fmt.Errorf("peer ip %q not found in peerdb", peerIP) } - return peerMac, vtep, nil + return peerMac, peerIPMask, vtep, nil } -func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP, +func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask, peerMac net.HardwareAddr, vtep net.IP, isLocal bool) { peerDbWg.Wait() @@ -149,9 +152,10 @@ func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP, } pEntry := peerEntry{ - eid: eid, - vtep: vtep, - isLocal: isLocal, + eid: eid, + vtep: vtep, + peerIPMask: peerIPMask, + isLocal: isLocal, } pMap.Lock() @@ -159,7 +163,7 @@ func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP, pMap.Unlock() } -func (d *driver) peerDbDelete(nid, eid string, peerIP net.IP, +func (d *driver) peerDbDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask, peerMac net.HardwareAddr, vtep net.IP) { peerDbWg.Wait() @@ -209,7 +213,7 @@ func (d *driver) peerDbUpdateSandbox(nid string) { // a copy of pEntry before capturing it in the following closure. entry := pEntry op := func() { - if err := d.peerAdd(nid, entry.eid, pKey.peerIP, + if err := d.peerAdd(nid, entry.eid, pKey.peerIP, entry.peerIPMask, pKey.peerMac, entry.vtep, false); err != nil { fmt.Printf("peerdbupdate in sandbox failed for ip %s and mac %s: %v", @@ -228,7 +232,7 @@ func (d *driver) peerDbUpdateSandbox(nid string) { peerDbWg.Done() } -func (d *driver) peerAdd(nid, eid string, peerIP net.IP, +func (d *driver) peerAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask, peerMac net.HardwareAddr, vtep net.IP, updateDb bool) error { if err := validateID(nid, eid); err != nil { @@ -236,7 +240,7 @@ func (d *driver) peerAdd(nid, eid string, peerIP net.IP, } if updateDb { - d.peerDbAdd(nid, eid, peerIP, peerMac, vtep, false) + d.peerDbAdd(nid, eid, peerIP, peerIPMask, peerMac, vtep, false) } n := d.network(nid) @@ -249,13 +253,31 @@ func (d *driver) peerAdd(nid, eid string, peerIP net.IP, return nil } + IP := &net.IPNet{ + IP: peerIP, + Mask: peerIPMask, + } + + s := n.getSubnetforIP(IP) + if s == nil { + return fmt.Errorf("couldn't find the subnet %q in network %q\n", IP.String(), n.id) + } + + if err := n.obtainVxlanID(s); err != nil { + return fmt.Errorf("couldn't get vxlan id for %q: %v", s.subnetIP.String(), err) + } + + if err := n.joinSubnetSandbox(s); err != nil { + return fmt.Errorf("subnet sandbox join failed for %q: %v", s.subnetIP.String(), err) + } + // Add neighbor entry for the peer IP - if err := sbox.AddNeighbor(peerIP, peerMac, sbox.NeighborOptions().LinkName(n.vxlanName)); err != nil { + if err := sbox.AddNeighbor(peerIP, peerMac, sbox.NeighborOptions().LinkName(s.vxlanName)); err != nil { return fmt.Errorf("could not add neigbor entry into the sandbox: %v", err) } // Add fdb entry to the bridge for the peer mac - if err := sbox.AddNeighbor(vtep, peerMac, sbox.NeighborOptions().LinkName(n.vxlanName), + if err := sbox.AddNeighbor(vtep, peerMac, sbox.NeighborOptions().LinkName(s.vxlanName), sbox.NeighborOptions().Family(syscall.AF_BRIDGE)); err != nil { return fmt.Errorf("could not add fdb entry into the sandbox: %v", err) } @@ -263,7 +285,7 @@ func (d *driver) peerAdd(nid, eid string, peerIP net.IP, return nil } -func (d *driver) peerDelete(nid, eid string, peerIP net.IP, +func (d *driver) peerDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask, peerMac net.HardwareAddr, vtep net.IP, updateDb bool) error { if err := validateID(nid, eid); err != nil { @@ -271,7 +293,7 @@ func (d *driver) peerDelete(nid, eid string, peerIP net.IP, } if updateDb { - d.peerDbDelete(nid, eid, peerIP, peerMac, vtep) + d.peerDbDelete(nid, eid, peerIP, peerIPMask, peerMac, vtep) } n := d.network(nid) diff --git a/vendor/src/github.com/docker/libnetwork/drivers/remote/api/api.go b/vendor/src/github.com/docker/libnetwork/drivers/remote/api/api.go index 2345be0a29..1d7742e21a 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/remote/api/api.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/remote/api/api.go @@ -34,6 +34,9 @@ type CreateNetworkRequest struct { // A free form map->object interface for communication of options. Options map[string]interface{} + + // IPAMData contains the address pool information for this network + IPv4Data, IPv6Data []driverapi.IPAMData } // CreateNetworkResponse is the response to the CreateNetworkRequest. diff --git a/vendor/src/github.com/docker/libnetwork/drivers/remote/driver.go b/vendor/src/github.com/docker/libnetwork/drivers/remote/driver.go index c4eb5e95c8..a10698e317 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/remote/driver.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/remote/driver.go @@ -82,10 +82,12 @@ func (d *driver) call(methodName string, arg interface{}, retVal maybeError) err return nil } -func (d *driver) CreateNetwork(id string, options map[string]interface{}) error { +func (d *driver) CreateNetwork(id string, options map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error { create := &api.CreateNetworkRequest{ NetworkID: id, Options: options, + IPv4Data: ipV4Data, + IPv6Data: ipV6Data, } return d.call("CreateNetwork", create, &api.CreateNetworkResponse{}) } @@ -95,23 +97,22 @@ func (d *driver) DeleteNetwork(nid string) error { return d.call("DeleteNetwork", delete, &api.DeleteNetworkResponse{}) } -func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { - var reqIface *api.EndpointInterface - - if epInfo == nil { - return fmt.Errorf("must not be called with nil EndpointInfo") +func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error { + if ifInfo == nil { + return fmt.Errorf("must not be called with nil InterfaceInfo") } - iface := epInfo.Interface() - if iface != nil { - addr4 := iface.Address() - addr6 := iface.AddressIPv6() - reqIface = &api.EndpointInterface{ - Address: addr4.String(), - AddressIPv6: addr6.String(), - MacAddress: iface.MacAddress().String(), - } + reqIface := &api.EndpointInterface{} + if ifInfo.Address() != nil { + reqIface.Address = ifInfo.Address().String() } + if ifInfo.AddressIPv6() != nil { + reqIface.AddressIPv6 = ifInfo.AddressIPv6().String() + } + if ifInfo.MacAddress() != nil { + reqIface.MacAddress = ifInfo.MacAddress().String() + } + create := &api.CreateEndpointRequest{ NetworkID: nid, EndpointID: eid, @@ -127,24 +128,27 @@ func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, if err != nil { return err } - if reqIface != nil && inIface != nil { - // We're not supposed to add interface if there is already - // one. Attempt to roll back - return errorWithRollback("driver attempted to add interface ignoring the one provided", d.DeleteEndpoint(nid, eid)) + if inIface == nil { + // Remote driver did not set any field + return nil } - if inIface != nil { - var addr4, addr6 net.IPNet - if inIface.Address != nil { - addr4 = *(inIface.Address) - } - if inIface.AddressIPv6 != nil { - addr6 = *(inIface.AddressIPv6) - } - if err := epInfo.AddInterface(inIface.MacAddress, addr4, addr6); err != nil { - return errorWithRollback(fmt.Sprintf("failed to AddInterface %v: %s", inIface, err), d.DeleteEndpoint(nid, eid)) + if inIface.MacAddress != nil { + if err := ifInfo.SetMacAddress(inIface.MacAddress); err != nil { + return errorWithRollback(fmt.Sprintf("driver modified interface MAC address: %v", err), d.DeleteEndpoint(nid, eid)) } } + if inIface.Address != nil { + if err := ifInfo.SetIPAddress(inIface.Address); err != nil { + return errorWithRollback(fmt.Sprintf("driver modified interface address: %v", err), d.DeleteEndpoint(nid, eid)) + } + } + if inIface.AddressIPv6 != nil { + if err := ifInfo.SetIPAddress(inIface.AddressIPv6); err != nil { + return errorWithRollback(fmt.Sprintf("driver modified interface address: %v", err), d.DeleteEndpoint(nid, eid)) + } + } + return nil } @@ -193,11 +197,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, } ifaceName := res.InterfaceName - if jinfo.InterfaceName() != nil && ifaceName == nil { - return fmt.Errorf("no interface name information received while one is expected") - } - - if iface := jinfo.InterfaceName(); iface != nil { + if iface := jinfo.InterfaceName(); iface != nil && ifaceName != nil { if err := iface.SetNames(ifaceName.SrcName, ifaceName.DstPrefix); err != nil { return errorWithRollback(fmt.Sprintf("failed to set interface name: %s", err), d.Leave(nid, eid)) } @@ -278,7 +278,7 @@ func parseStaticRoutes(r api.JoinResponse) ([]*types.StaticRoute, error) { outRoute := &types.StaticRoute{RouteType: inRoute.RouteType} if inRoute.Destination != "" { - if outRoute.Destination, err = toAddr(inRoute.Destination); err != nil { + if outRoute.Destination, err = types.ParseCIDR(inRoute.Destination); err != nil { return nil, err } } @@ -304,12 +304,12 @@ func parseInterface(r api.CreateEndpointResponse) (*api.Interface, error) { var err error outIf = &api.Interface{} if inIf.Address != "" { - if outIf.Address, err = toAddr(inIf.Address); err != nil { + if outIf.Address, err = types.ParseCIDR(inIf.Address); err != nil { return nil, err } } if inIf.AddressIPv6 != "" { - if outIf.AddressIPv6, err = toAddr(inIf.AddressIPv6); err != nil { + if outIf.AddressIPv6, err = types.ParseCIDR(inIf.AddressIPv6); err != nil { return nil, err } } @@ -322,12 +322,3 @@ func parseInterface(r api.CreateEndpointResponse) (*api.Interface, error) { return outIf, nil } - -func toAddr(ipAddr string) (*net.IPNet, error) { - ip, ipnet, err := net.ParseCIDR(ipAddr) - if err != nil { - return nil, err - } - ipnet.IP = ip - return ipnet, nil -} diff --git a/vendor/src/github.com/docker/libnetwork/drivers/windows/windows.go b/vendor/src/github.com/docker/libnetwork/drivers/windows/windows.go index 6872486bf4..5464a5f070 100644 --- a/vendor/src/github.com/docker/libnetwork/drivers/windows/windows.go +++ b/vendor/src/github.com/docker/libnetwork/drivers/windows/windows.go @@ -19,7 +19,7 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error { return dc.RegisterDriver(networkType, &driver{}, c) } -func (d *driver) CreateNetwork(id string, option map[string]interface{}) error { +func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error { return nil } @@ -27,7 +27,7 @@ func (d *driver) DeleteNetwork(nid string) error { return nil } -func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error { +func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error { return nil } diff --git a/vendor/src/github.com/docker/libnetwork/endpoint.go b/vendor/src/github.com/docker/libnetwork/endpoint.go index 83db4a8e7e..40459369f7 100644 --- a/vendor/src/github.com/docker/libnetwork/endpoint.go +++ b/vendor/src/github.com/docker/libnetwork/endpoint.go @@ -10,7 +10,9 @@ import ( log "github.com/Sirupsen/logrus" "github.com/docker/libnetwork/datastore" + "github.com/docker/libnetwork/ipamapi" "github.com/docker/libnetwork/netlabel" + "github.com/docker/libnetwork/options" "github.com/docker/libnetwork/types" ) @@ -71,7 +73,9 @@ func (ep *endpoint) MarshalJSON() ([]byte, error) { epMap["id"] = ep.id epMap["ep_iface"] = ep.iface epMap["exposed_ports"] = ep.exposedPorts - epMap["generic"] = ep.generic + if ep.generic != nil { + epMap["generic"] = ep.generic + } epMap["sandbox"] = ep.sandboxID return json.Marshal(epMap) } @@ -98,12 +102,43 @@ func (ep *endpoint) UnmarshalJSON(b []byte) (err error) { cb, _ := json.Marshal(epMap["sandbox"]) json.Unmarshal(cb, &ep.sandboxID) - if epMap["generic"] != nil { - ep.generic = epMap["generic"].(map[string]interface{}) + if v, ok := epMap["generic"]; ok { + ep.generic = v.(map[string]interface{}) } return nil } +func (ep *endpoint) New() datastore.KVObject { + return &endpoint{network: ep.getNetwork()} +} + +func (ep *endpoint) CopyTo(o datastore.KVObject) error { + ep.Lock() + defer ep.Unlock() + + dstEp := o.(*endpoint) + dstEp.name = ep.name + dstEp.id = ep.id + dstEp.sandboxID = ep.sandboxID + dstEp.dbIndex = ep.dbIndex + dstEp.dbExists = ep.dbExists + + if ep.iface != nil { + dstEp.iface = &endpointInterface{} + ep.iface.CopyTo(dstEp.iface) + } + + dstEp.exposedPorts = make([]types.TransportPort, len(ep.exposedPorts)) + copy(dstEp.exposedPorts, ep.exposedPorts) + + dstEp.generic = options.Generic{} + for k, v := range ep.generic { + dstEp.generic[k] = v + } + + return nil +} + func (ep *endpoint) ID() string { ep.Lock() defer ep.Unlock() @@ -119,16 +154,28 @@ func (ep *endpoint) Name() string { } func (ep *endpoint) Network() string { - return ep.getNetwork().name + if ep.network == nil { + return "" + } + + return ep.network.name } // endpoint Key structure : endpoint/network-id/endpoint-id func (ep *endpoint) Key() []string { - return []string{datastore.EndpointKeyPrefix, ep.getNetwork().id, ep.id} + if ep.network == nil { + return nil + } + + return []string{datastore.EndpointKeyPrefix, ep.network.id, ep.id} } func (ep *endpoint) KeyPrefix() []string { - return []string{datastore.EndpointKeyPrefix, ep.getNetwork().id} + if ep.network == nil { + return nil + } + + return []string{datastore.EndpointKeyPrefix, ep.network.id} } func (ep *endpoint) networkIDFromKey(key string) (string, error) { @@ -188,8 +235,22 @@ func (ep *endpoint) processOptions(options ...EndpointOption) { } } -func (ep *endpoint) Join(sbox Sandbox, options ...EndpointOption) error { +func (ep *endpoint) getNetwork() *network { + ep.Lock() + defer ep.Unlock() + return ep.network +} + +func (ep *endpoint) getNetworkFromStore() (*network, error) { + if ep.network == nil { + return nil, fmt.Errorf("invalid network object in endpoint %s", ep.Name()) + } + + return ep.network.ctrlr.getNetworkFromStore(ep.network.id) +} + +func (ep *endpoint) Join(sbox Sandbox, options ...EndpointOption) error { if sbox == nil { return types.BadRequestErrorf("endpoint cannot be joined by nil container") } @@ -212,15 +273,27 @@ func (ep *endpoint) sbJoin(sbox Sandbox, options ...EndpointOption) error { return types.BadRequestErrorf("not a valid Sandbox interface") } + network, err := ep.getNetworkFromStore() + if err != nil { + return fmt.Errorf("failed to get network from store during join: %v", err) + } + + ep, err = network.getEndpointFromStore(ep.ID()) + if err != nil { + return fmt.Errorf("failed to get endpoint from store during join: %v", err) + } + ep.Lock() if ep.sandboxID != "" { ep.Unlock() - return types.ForbiddenErrorf("a sandbox has already joined the endpoint") + return types.ForbiddenErrorf("another container is attached to the same network endpoint") } + ep.Unlock() + ep.Lock() + ep.network = network ep.sandboxID = sbox.ID() ep.joinInfo = &endpointJoinInfo{} - network := ep.network epid := ep.id ep.Unlock() defer func() { @@ -232,12 +305,16 @@ func (ep *endpoint) sbJoin(sbox Sandbox, options ...EndpointOption) error { }() network.Lock() - driver := network.driver nid := network.id network.Unlock() ep.processOptions(options...) + driver, err := network.driver() + if err != nil { + return fmt.Errorf("failed to join endpoint: %v", err) + } + err = driver.Join(nid, epid, sbox.Key(), ep, sbox.Labels()) if err != nil { return err @@ -259,14 +336,15 @@ func (ep *endpoint) sbJoin(sbox Sandbox, options ...EndpointOption) error { return err } - if err = sb.updateDNS(ep.getNetwork().enableIPv6); err != nil { + // Watch for service records + network.getController().watchSvcRecord(ep) + + if err = sb.updateDNS(network.enableIPv6); err != nil { return err } - if !ep.isLocalScoped() { - if err = network.ctrlr.updateToStore(ep); err != nil { - return err - } + if err = network.getController().updateToStore(ep); err != nil { + return err } sb.Lock() @@ -324,6 +402,16 @@ func (ep *endpoint) sbLeave(sbox Sandbox, options ...EndpointOption) error { return types.BadRequestErrorf("not a valid Sandbox interface") } + n, err := ep.getNetworkFromStore() + if err != nil { + return fmt.Errorf("failed to get network from store during leave: %v", err) + } + + ep, err = n.getEndpointFromStore(ep.ID()) + if err != nil { + return fmt.Errorf("failed to get endpoint from store during leave: %v", err) + } + ep.Lock() sid := ep.sandboxID ep.Unlock() @@ -339,21 +427,19 @@ func (ep *endpoint) sbLeave(sbox Sandbox, options ...EndpointOption) error { ep.Lock() ep.sandboxID = "" - n := ep.network + ep.network = n ep.Unlock() - n.Lock() - c := n.ctrlr - d := n.driver - n.Unlock() + if err := n.getController().updateToStore(ep); err != nil { + ep.Lock() + ep.sandboxID = sid + ep.Unlock() + return err + } - if !ep.isLocalScoped() { - if err := c.updateToStore(ep); err != nil { - ep.Lock() - ep.sandboxID = sid - ep.Unlock() - return err - } + d, err := n.driver() + if err != nil { + return fmt.Errorf("failed to leave endpoint: %v", err) } if err := d.Leave(n.id, ep.id); err != nil { @@ -364,6 +450,9 @@ func (ep *endpoint) sbLeave(sbox Sandbox, options ...EndpointOption) error { return err } + // unwatch for service records + n.getController().unWatchSvcRecord(ep) + if sb.needDefaultGW() { ep := sb.getEPwithoutGateway() if ep == nil { @@ -376,53 +465,54 @@ func (ep *endpoint) sbLeave(sbox Sandbox, options ...EndpointOption) error { func (ep *endpoint) Delete() error { var err error + n, err := ep.getNetworkFromStore() + if err != nil { + return fmt.Errorf("failed to get network during Delete: %v", err) + } + + ep, err = n.getEndpointFromStore(ep.ID()) + if err != nil { + return fmt.Errorf("failed to get endpoint from store during Delete: %v", err) + } + ep.Lock() epid := ep.id name := ep.name - n := ep.network if ep.sandboxID != "" { ep.Unlock() return &ActiveContainerError{name: name, id: epid} } - n.Lock() - ctrlr := n.ctrlr - n.Unlock() ep.Unlock() - if !ep.isLocalScoped() { - if err = ctrlr.deleteFromStore(ep); err != nil { - return err - } - } - defer func() { - if err != nil { - ep.dbExists = false - if !ep.isLocalScoped() { - if e := ctrlr.updateToStore(ep); e != nil { - log.Warnf("failed to recreate endpoint in store %s : %v", name, e) - } - } - } - }() - - // Update the endpoint count in network and update it in the datastore - n.DecEndpointCnt() - if err = ctrlr.updateToStore(n); err != nil { + if err = n.getEpCnt().DecEndpointCnt(); err != nil { return err } defer func() { if err != nil { - n.IncEndpointCnt() - if e := ctrlr.updateToStore(n); e != nil { + if e := n.getEpCnt().IncEndpointCnt(); e != nil { log.Warnf("failed to update network %s : %v", n.name, e) } } }() + if err = n.getController().deleteFromStore(ep); err != nil { + return err + } + defer func() { + if err != nil { + ep.dbExists = false + if e := n.getController().updateToStore(ep); e != nil { + log.Warnf("failed to recreate endpoint in store %s : %v", name, e) + } + } + }() + if err = ep.deleteEndpoint(); err != nil { return err } + ep.releaseAddress() + return nil } @@ -433,38 +523,21 @@ func (ep *endpoint) deleteEndpoint() error { epid := ep.id ep.Unlock() - n.Lock() - _, ok := n.endpoints[epid] - if !ok { - n.Unlock() - return nil + driver, err := n.driver() + if err != nil { + return fmt.Errorf("failed to delete endpoint: %v", err) } - nid := n.id - driver := n.driver - delete(n.endpoints, epid) - n.Unlock() - - if err := driver.DeleteEndpoint(nid, epid); err != nil { + if err := driver.DeleteEndpoint(n.id, epid); err != nil { if _, ok := err.(types.ForbiddenError); ok { - n.Lock() - n.endpoints[epid] = ep - n.Unlock() return err } log.Warnf("driver error deleting endpoint %s : %v", name, err) } - n.updateSvcRecord(ep, false) return nil } -func (ep *endpoint) getNetwork() *network { - ep.Lock() - defer ep.Unlock() - return ep.network -} - func (ep *endpoint) getSandbox() (*sandbox, bool) { ep.Lock() c := ep.network.getController() @@ -482,7 +555,7 @@ func (ep *endpoint) getFirstInterfaceAddress() net.IP { ep.Lock() defer ep.Unlock() - if ep.iface != nil { + if ep.iface.addr != nil { return ep.iface.addr.IP } @@ -540,12 +613,94 @@ func JoinOptionPriority(ep Endpoint, prio int) EndpointOption { } } -func (ep *endpoint) DataScope() datastore.DataScope { - ep.Lock() - defer ep.Unlock() - return ep.network.dataScope +func (ep *endpoint) DataScope() string { + return ep.getNetwork().DataScope() } -func (ep *endpoint) isLocalScoped() bool { - return ep.DataScope() == datastore.LocalScope +func (ep *endpoint) assignAddress() error { + var ( + ipam ipamapi.Ipam + err error + ) + + n := ep.getNetwork() + if n.Type() == "host" || n.Type() == "null" { + return nil + } + + log.Debugf("Assigning addresses for endpoint %s's interface on network %s", ep.Name(), n.Name()) + + ipam, err = n.getController().getIpamDriver(n.ipamType) + if err != nil { + return err + } + err = ep.assignAddressVersion(4, ipam) + if err != nil { + return err + } + return ep.assignAddressVersion(6, ipam) +} + +func (ep *endpoint) assignAddressVersion(ipVer int, ipam ipamapi.Ipam) error { + var ( + poolID *string + address **net.IPNet + ) + + n := ep.getNetwork() + switch ipVer { + case 4: + poolID = &ep.iface.v4PoolID + address = &ep.iface.addr + case 6: + poolID = &ep.iface.v6PoolID + address = &ep.iface.addrv6 + default: + return types.InternalErrorf("incorrect ip version number passed: %d", ipVer) + } + + ipInfo := n.getIPInfo(ipVer) + + // ipv6 address is not mandatory + if len(ipInfo) == 0 && ipVer == 6 { + return nil + } + + for _, d := range ipInfo { + addr, _, err := ipam.RequestAddress(d.PoolID, nil, nil) + if err == nil { + ep.Lock() + *address = addr + *poolID = d.PoolID + ep.Unlock() + return nil + } + if err != ipamapi.ErrNoAvailableIPs { + return err + } + } + return fmt.Errorf("no available IPv%d addresses on this network's address pools: %s (%s)", ipVer, n.Name(), n.ID()) +} + +func (ep *endpoint) releaseAddress() { + n := ep.getNetwork() + if n.Type() == "host" || n.Type() == "null" { + return + } + + log.Debugf("Releasing addresses for endpoint %s's interface on network %s", ep.Name(), n.Name()) + + ipam, err := n.getController().getIpamDriver(n.ipamType) + if err != nil { + log.Warnf("Failed to retrieve ipam driver to release interface address on delete of endpoint %s (%s): %v", ep.Name(), ep.ID(), err) + return + } + if err := ipam.ReleaseAddress(ep.iface.v4PoolID, ep.iface.addr.IP); err != nil { + log.Warnf("Failed to release ip address %s on delete of endpoint %s (%s): %v", ep.iface.addr.IP, ep.Name(), ep.ID(), err) + } + if ep.iface.addrv6 != nil && ep.iface.addrv6.IP.IsGlobalUnicast() { + if err := ipam.ReleaseAddress(ep.iface.v6PoolID, ep.iface.addrv6.IP); err != nil { + log.Warnf("Failed to release ip address %s on delete of endpoint %s (%s): %v", ep.iface.addrv6.IP, ep.Name(), ep.ID(), err) + } + } } diff --git a/vendor/src/github.com/docker/libnetwork/endpoint_cnt.go b/vendor/src/github.com/docker/libnetwork/endpoint_cnt.go new file mode 100644 index 0000000000..550a2a3cfd --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/endpoint_cnt.go @@ -0,0 +1,147 @@ +package libnetwork + +import ( + "encoding/json" + "fmt" + "sync" + + "github.com/docker/libnetwork/datastore" +) + +type endpointCnt struct { + n *network + Count uint64 + dbIndex uint64 + dbExists bool + sync.Mutex +} + +const epCntKeyPrefix = "endpoint_count" + +func (ec *endpointCnt) Key() []string { + ec.Lock() + defer ec.Unlock() + + return []string{epCntKeyPrefix, ec.n.id} +} + +func (ec *endpointCnt) KeyPrefix() []string { + ec.Lock() + defer ec.Unlock() + + return []string{epCntKeyPrefix, ec.n.id} +} + +func (ec *endpointCnt) Value() []byte { + ec.Lock() + defer ec.Unlock() + + b, err := json.Marshal(ec) + if err != nil { + return nil + } + return b +} + +func (ec *endpointCnt) SetValue(value []byte) error { + ec.Lock() + defer ec.Unlock() + + return json.Unmarshal(value, &ec) +} + +func (ec *endpointCnt) Index() uint64 { + ec.Lock() + defer ec.Unlock() + return ec.dbIndex +} + +func (ec *endpointCnt) SetIndex(index uint64) { + ec.Lock() + ec.dbIndex = index + ec.dbExists = true + ec.Unlock() +} + +func (ec *endpointCnt) Exists() bool { + ec.Lock() + defer ec.Unlock() + return ec.dbExists +} + +func (ec *endpointCnt) Skip() bool { + ec.Lock() + defer ec.Unlock() + return !ec.n.persist +} + +func (ec *endpointCnt) New() datastore.KVObject { + ec.Lock() + defer ec.Unlock() + + return &endpointCnt{ + n: ec.n, + } +} + +func (ec *endpointCnt) CopyTo(o datastore.KVObject) error { + ec.Lock() + defer ec.Unlock() + + dstEc := o.(*endpointCnt) + dstEc.n = ec.n + dstEc.Count = ec.Count + dstEc.dbExists = ec.dbExists + dstEc.dbIndex = ec.dbIndex + + return nil +} + +func (ec *endpointCnt) DataScope() string { + return ec.n.DataScope() +} + +func (ec *endpointCnt) EndpointCnt() uint64 { + ec.Lock() + defer ec.Unlock() + + return ec.Count +} + +func (ec *endpointCnt) atomicIncDecEpCnt(inc bool) error { +retry: + ec.Lock() + if inc { + ec.Count++ + } else { + ec.Count-- + } + ec.Unlock() + + store := ec.n.getController().getStore(ec.DataScope()) + if store == nil { + return fmt.Errorf("store not found for scope %s", ec.DataScope()) + } + + if err := ec.n.getController().updateToStore(ec); err != nil { + if err == datastore.ErrKeyModified { + if err := store.GetObject(datastore.Key(ec.Key()...), ec); err != nil { + return fmt.Errorf("could not update the kvobject to latest when trying to atomic add endpoint count: %v", err) + } + + goto retry + } + + return err + } + + return nil +} + +func (ec *endpointCnt) IncEndpointCnt() error { + return ec.atomicIncDecEpCnt(true) +} + +func (ec *endpointCnt) DecEndpointCnt() error { + return ec.atomicIncDecEpCnt(false) +} diff --git a/vendor/src/github.com/docker/libnetwork/endpoint_info.go b/vendor/src/github.com/docker/libnetwork/endpoint_info.go index 6d06675ae9..3ca6b2b834 100644 --- a/vendor/src/github.com/docker/libnetwork/endpoint_info.go +++ b/vendor/src/github.com/docker/libnetwork/endpoint_info.go @@ -2,6 +2,7 @@ package libnetwork import ( "encoding/json" + "fmt" "net" "github.com/docker/libnetwork/driverapi" @@ -34,26 +35,34 @@ type InterfaceInfo interface { MacAddress() net.HardwareAddr // Address returns the IPv4 address assigned to the endpoint. - Address() net.IPNet + Address() *net.IPNet // AddressIPv6 returns the IPv6 address assigned to the endpoint. - AddressIPv6() net.IPNet + AddressIPv6() *net.IPNet } type endpointInterface struct { mac net.HardwareAddr - addr net.IPNet - addrv6 net.IPNet + addr *net.IPNet + addrv6 *net.IPNet srcName string dstPrefix string routes []*net.IPNet + v4PoolID string + v6PoolID string } func (epi *endpointInterface) MarshalJSON() ([]byte, error) { epMap := make(map[string]interface{}) - epMap["mac"] = epi.mac.String() - epMap["addr"] = epi.addr.String() - epMap["addrv6"] = epi.addrv6.String() + if epi.mac != nil { + epMap["mac"] = epi.mac.String() + } + if epi.addr != nil { + epMap["addr"] = epi.addr.String() + } + if epi.addrv6 != nil { + epMap["addrv6"] = epi.addrv6.String() + } epMap["srcName"] = epi.srcName epMap["dstPrefix"] = epi.dstPrefix var routes []string @@ -61,28 +70,33 @@ func (epi *endpointInterface) MarshalJSON() ([]byte, error) { routes = append(routes, route.String()) } epMap["routes"] = routes + epMap["v4PoolID"] = epi.v4PoolID + epMap["v6PoolID"] = epi.v6PoolID return json.Marshal(epMap) } -func (epi *endpointInterface) UnmarshalJSON(b []byte) (err error) { - var epMap map[string]interface{} - if err := json.Unmarshal(b, &epMap); err != nil { +func (epi *endpointInterface) UnmarshalJSON(b []byte) error { + var ( + err error + epMap map[string]interface{} + ) + if err = json.Unmarshal(b, &epMap); err != nil { return err } - - mac, _ := net.ParseMAC(epMap["mac"].(string)) - epi.mac = mac - - ip, ipnet, _ := net.ParseCIDR(epMap["addr"].(string)) - if ipnet != nil { - ipnet.IP = ip - epi.addr = *ipnet + if v, ok := epMap["mac"]; ok { + if epi.mac, err = net.ParseMAC(v.(string)); err != nil { + return types.InternalErrorf("failed to decode endpoint interface mac address after json unmarshal: %s", v.(string)) + } } - - ip, ipnet, _ = net.ParseCIDR(epMap["addrv6"].(string)) - if ipnet != nil { - ipnet.IP = ip - epi.addrv6 = *ipnet + if v, ok := epMap["addr"]; ok { + if epi.addr, err = types.ParseCIDR(v.(string)); err != nil { + return types.InternalErrorf("failed to decode endpoint interface ipv4 address after json unmarshal: %v", err) + } + } + if v, ok := epMap["addrv6"]; ok { + if epi.addrv6, err = types.ParseCIDR(v.(string)); err != nil { + return types.InternalErrorf("failed to decode endpoint interface ipv6 address after json unmarshal: %v", err) + } } epi.srcName = epMap["srcName"].(string) @@ -99,6 +113,24 @@ func (epi *endpointInterface) UnmarshalJSON(b []byte) (err error) { epi.routes = append(epi.routes, ipr) } } + epi.v4PoolID = epMap["v4PoolID"].(string) + epi.v6PoolID = epMap["v6PoolID"].(string) + + return nil +} + +func (epi *endpointInterface) CopyTo(dstEpi *endpointInterface) error { + dstEpi.mac = types.GetMacCopy(epi.mac) + dstEpi.addr = types.GetIPNetCopy(epi.addr) + dstEpi.addrv6 = types.GetIPNetCopy(epi.addrv6) + dstEpi.srcName = epi.srcName + dstEpi.dstPrefix = epi.dstPrefix + dstEpi.v4PoolID = epi.v4PoolID + dstEpi.v6PoolID = epi.v6PoolID + + for _, route := range epi.routes { + dstEpi.routes = append(dstEpi.routes, types.GetIPNetCopy(route)) + } return nil } @@ -110,21 +142,38 @@ type endpointJoinInfo struct { } func (ep *endpoint) Info() EndpointInfo { - return ep + n, err := ep.getNetworkFromStore() + if err != nil { + return nil + } + + ep, err = n.getEndpointFromStore(ep.ID()) + if err != nil { + return nil + } + + sb, ok := ep.getSandbox() + if !ok { + // endpoint hasn't joined any sandbox. + // Just return the endpoint + return ep + } + + return sb.getEndpoint(ep.ID()) } func (ep *endpoint) DriverInfo() (map[string]interface{}, error) { - ep.Lock() - network := ep.network - epid := ep.id - ep.Unlock() + n, err := ep.getNetworkFromStore() + if err != nil { + return nil, fmt.Errorf("could not find network in store for driver info: %v", err) + } - network.Lock() - driver := network.driver - nid := network.id - network.Unlock() + driver, err := n.driver() + if err != nil { + return nil, fmt.Errorf("failed to get driver info: %v", err) + } - return driver.EndpointOperInfo(nid, epid) + return driver.EndpointOperInfo(n.ID(), ep.ID()) } func (ep *endpoint) Iface() InterfaceInfo { @@ -149,17 +198,32 @@ func (ep *endpoint) Interface() driverapi.InterfaceInfo { return nil } -func (ep *endpoint) AddInterface(mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error { - ep.Lock() - defer ep.Unlock() - - iface := &endpointInterface{ - addr: *types.GetIPNetCopy(&ipv4), - addrv6: *types.GetIPNetCopy(&ipv6), +func (epi *endpointInterface) SetMacAddress(mac net.HardwareAddr) error { + if epi.mac != nil { + return types.ForbiddenErrorf("endpoint interface MAC address present (%s). Cannot be modified with %s.", epi.mac, mac) } - iface.mac = types.GetMacCopy(mac) + if mac == nil { + return types.BadRequestErrorf("tried to set nil MAC address to endpoint interface") + } + epi.mac = types.GetMacCopy(mac) + return nil +} - ep.iface = iface +func (epi *endpointInterface) SetIPAddress(address *net.IPNet) error { + if address.IP == nil { + return types.BadRequestErrorf("tried to set nil IP address to endpoint interface") + } + if address.IP.To4() == nil { + return setAddress(&epi.addrv6, address) + } + return setAddress(&epi.addr, address) +} + +func setAddress(ifaceAddr **net.IPNet, address *net.IPNet) error { + if *ifaceAddr != nil { + return types.ForbiddenErrorf("endpoint interface IP present (%s). Cannot be modified with (%s).", *ifaceAddr, address) + } + *ifaceAddr = types.GetIPNetCopy(address) return nil } @@ -167,12 +231,12 @@ func (epi *endpointInterface) MacAddress() net.HardwareAddr { return types.GetMacCopy(epi.mac) } -func (epi *endpointInterface) Address() net.IPNet { - return (*types.GetIPNetCopy(&epi.addr)) +func (epi *endpointInterface) Address() *net.IPNet { + return types.GetIPNetCopy(epi.addr) } -func (epi *endpointInterface) AddressIPv6() net.IPNet { - return (*types.GetIPNetCopy(&epi.addrv6)) +func (epi *endpointInterface) AddressIPv6() *net.IPNet { + return types.GetIPNetCopy(epi.addrv6) } func (epi *endpointInterface) SetNames(srcName string, dstPrefix string) error { diff --git a/vendor/src/github.com/docker/libnetwork/error.go b/vendor/src/github.com/docker/libnetwork/error.go index d31a50948b..4158c9956c 100644 --- a/vendor/src/github.com/docker/libnetwork/error.go +++ b/vendor/src/github.com/docker/libnetwork/error.go @@ -129,7 +129,7 @@ type ActiveEndpointsError struct { } func (aee *ActiveEndpointsError) Error() string { - return fmt.Sprintf("network with name %s id %s has active endpoints", aee.name, aee.id) + return fmt.Sprintf("network %s has active endpoints", aee.name) } // Forbidden denotes the type of this error diff --git a/vendor/src/github.com/docker/libnetwork/idm/idm.go b/vendor/src/github.com/docker/libnetwork/idm/idm.go index 8c15cfe4ec..37ae79e558 100644 --- a/vendor/src/github.com/docker/libnetwork/idm/idm.go +++ b/vendor/src/github.com/docker/libnetwork/idm/idm.go @@ -10,13 +10,13 @@ import ( // Idm manages the reservation/release of numerical ids from a contiguos set type Idm struct { - start uint32 - end uint32 + start uint64 + end uint64 handle *bitseq.Handle } // New returns an instance of id manager for a set of [start-end] numerical ids -func New(ds datastore.DataStore, id string, start, end uint32) (*Idm, error) { +func New(ds datastore.DataStore, id string, start, end uint64) (*Idm, error) { if id == "" { return nil, fmt.Errorf("Invalid id") } @@ -33,7 +33,7 @@ func New(ds datastore.DataStore, id string, start, end uint32) (*Idm, error) { } // GetID returns the first available id in the set -func (i *Idm) GetID() (uint32, error) { +func (i *Idm) GetID() (uint64, error) { if i.handle == nil { return 0, fmt.Errorf("ID set is not initialized") } @@ -42,7 +42,7 @@ func (i *Idm) GetID() (uint32, error) { } // GetSpecificID tries to reserve the specified id -func (i *Idm) GetSpecificID(id uint32) error { +func (i *Idm) GetSpecificID(id uint64) error { if i.handle == nil { return fmt.Errorf("ID set is not initialized") } @@ -55,6 +55,6 @@ func (i *Idm) GetSpecificID(id uint32) error { } // Release releases the specified id -func (i *Idm) Release(id uint32) { +func (i *Idm) Release(id uint64) { i.handle.Unset(id - i.start) } diff --git a/vendor/src/github.com/docker/libnetwork/ipallocator/allocator.go b/vendor/src/github.com/docker/libnetwork/ipallocator/allocator.go deleted file mode 100644 index 06bc051c55..0000000000 --- a/vendor/src/github.com/docker/libnetwork/ipallocator/allocator.go +++ /dev/null @@ -1,175 +0,0 @@ -// Package ipallocator defines the default IP allocator. It will move out of libnetwork as an external IPAM plugin. -// This has been imported unchanged from Docker, besides additon of registration logic -package ipallocator - -import ( - "errors" - "math/big" - "net" - "sync" - - "github.com/Sirupsen/logrus" - "github.com/docker/libnetwork/netutils" -) - -// allocatedMap is thread-unsafe set of allocated IP -type allocatedMap struct { - p map[string]struct{} - last *big.Int - begin *big.Int - end *big.Int -} - -func newAllocatedMap(network *net.IPNet) *allocatedMap { - firstIP, lastIP := netutils.NetworkRange(network) - begin := big.NewInt(0).Add(ipToBigInt(firstIP), big.NewInt(1)) - end := big.NewInt(0).Sub(ipToBigInt(lastIP), big.NewInt(1)) - - return &allocatedMap{ - p: make(map[string]struct{}), - begin: begin, - end: end, - last: big.NewInt(0).Sub(begin, big.NewInt(1)), // so first allocated will be begin - } -} - -type networkSet map[string]*allocatedMap - -var ( - // ErrNoAvailableIPs preformatted error - ErrNoAvailableIPs = errors.New("no available ip addresses on network") - // ErrIPAlreadyAllocated preformatted error - ErrIPAlreadyAllocated = errors.New("ip already allocated") - // ErrIPOutOfRange preformatted error - ErrIPOutOfRange = errors.New("requested ip is out of range") - // ErrNetworkAlreadyRegistered preformatted error - ErrNetworkAlreadyRegistered = errors.New("network already registered") - // ErrBadSubnet preformatted error - ErrBadSubnet = errors.New("network does not contain specified subnet") -) - -// IPAllocator manages the ipam -type IPAllocator struct { - allocatedIPs networkSet - mutex sync.Mutex -} - -// New returns a new instance of IPAllocator -func New() *IPAllocator { - return &IPAllocator{networkSet{}, sync.Mutex{}} -} - -// RegisterSubnet registers network in global allocator with bounds -// defined by subnet. If you want to use network range you must call -// this method before first RequestIP, otherwise full network range will be used -func (a *IPAllocator) RegisterSubnet(network *net.IPNet, subnet *net.IPNet) error { - a.mutex.Lock() - defer a.mutex.Unlock() - - nw := &net.IPNet{IP: network.IP.Mask(network.Mask), Mask: network.Mask} - key := nw.String() - if _, ok := a.allocatedIPs[key]; ok { - return ErrNetworkAlreadyRegistered - } - - // Check that subnet is within network - beginIP, endIP := netutils.NetworkRange(subnet) - if !(network.Contains(beginIP) && network.Contains(endIP)) { - return ErrBadSubnet - } - - n := newAllocatedMap(subnet) - a.allocatedIPs[key] = n - return nil -} - -// RequestIP requests an available ip from the given network. It -// will return the next available ip if the ip provided is nil. If the -// ip provided is not nil it will validate that the provided ip is available -// for use or return an error -func (a *IPAllocator) RequestIP(network *net.IPNet, ip net.IP) (net.IP, error) { - a.mutex.Lock() - defer a.mutex.Unlock() - - nw := &net.IPNet{IP: network.IP.Mask(network.Mask), Mask: network.Mask} - key := nw.String() - allocated, ok := a.allocatedIPs[key] - if !ok { - allocated = newAllocatedMap(nw) - a.allocatedIPs[key] = allocated - } - - if ip == nil { - return allocated.getNextIP() - } - return allocated.checkIP(ip) -} - -// ReleaseIP adds the provided ip back into the pool of -// available ips to be returned for use. -func (a *IPAllocator) ReleaseIP(network *net.IPNet, ip net.IP) error { - a.mutex.Lock() - defer a.mutex.Unlock() - - nw := &net.IPNet{IP: network.IP.Mask(network.Mask), Mask: network.Mask} - if allocated, exists := a.allocatedIPs[nw.String()]; exists { - delete(allocated.p, ip.String()) - } - return nil -} - -func (allocated *allocatedMap) checkIP(ip net.IP) (net.IP, error) { - if _, ok := allocated.p[ip.String()]; ok { - return nil, ErrIPAlreadyAllocated - } - - pos := ipToBigInt(ip) - // Verify that the IP address is within our network range. - if pos.Cmp(allocated.begin) == -1 || pos.Cmp(allocated.end) == 1 { - return nil, ErrIPOutOfRange - } - - // Register the IP. - allocated.p[ip.String()] = struct{}{} - - return ip, nil -} - -// return an available ip if one is currently available. If not, -// return the next available ip for the network -func (allocated *allocatedMap) getNextIP() (net.IP, error) { - pos := big.NewInt(0).Set(allocated.last) - allRange := big.NewInt(0).Sub(allocated.end, allocated.begin) - for i := big.NewInt(0); i.Cmp(allRange) <= 0; i.Add(i, big.NewInt(1)) { - pos.Add(pos, big.NewInt(1)) - if pos.Cmp(allocated.end) == 1 { - pos.Set(allocated.begin) - } - if _, ok := allocated.p[bigIntToIP(pos).String()]; ok { - continue - } - allocated.p[bigIntToIP(pos).String()] = struct{}{} - allocated.last.Set(pos) - return bigIntToIP(pos), nil - } - return nil, ErrNoAvailableIPs -} - -// Converts a 4 bytes IP into a 128 bit integer -func ipToBigInt(ip net.IP) *big.Int { - x := big.NewInt(0) - if ip4 := ip.To4(); ip4 != nil { - return x.SetBytes(ip4) - } - if ip6 := ip.To16(); ip6 != nil { - return x.SetBytes(ip6) - } - - logrus.Errorf("ipToBigInt: Wrong IP length! %s", ip) - return nil -} - -// Converts 128 bit integer into a 4 bytes IP address -func bigIntToIP(v *big.Int) net.IP { - return net.IP(v.Bytes()) -} diff --git a/vendor/src/github.com/docker/libnetwork/ipam/allocator.go b/vendor/src/github.com/docker/libnetwork/ipam/allocator.go new file mode 100644 index 0000000000..119031da89 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/ipam/allocator.go @@ -0,0 +1,510 @@ +package ipam + +import ( + "fmt" + "net" + "sync" + + log "github.com/Sirupsen/logrus" + "github.com/docker/libnetwork/bitseq" + "github.com/docker/libnetwork/datastore" + "github.com/docker/libnetwork/ipamapi" + "github.com/docker/libnetwork/ipamutils" + "github.com/docker/libnetwork/types" +) + +const ( + localAddressSpace = "LocalDefault" + globalAddressSpace = "GlobalDefault" + // The biggest configurable host subnets + minNetSize = 8 + minNetSizeV6 = 64 + // datastore keyes for ipam objects + dsConfigKey = "ipam/" + ipamapi.DefaultIPAM + "/config" + dsDataKey = "ipam/" + ipamapi.DefaultIPAM + "/data" +) + +// Allocator provides per address space ipv4/ipv6 book keeping +type Allocator struct { + // Predefined pools for default address spaces + predefined map[string][]*net.IPNet + addrSpaces map[string]*addrSpace + // stores []datastore.Datastore + // Allocated addresses in each address space's subnet + addresses map[SubnetKey]*bitseq.Handle + sync.Mutex +} + +// NewAllocator returns an instance of libnetwork ipam +func NewAllocator(lcDs, glDs datastore.DataStore) (*Allocator, error) { + a := &Allocator{} + + // Load predefined subnet pools + a.predefined = map[string][]*net.IPNet{ + localAddressSpace: ipamutils.PredefinedBroadNetworks, + globalAddressSpace: ipamutils.PredefinedGranularNetworks, + } + + // Initialize bitseq map + a.addresses = make(map[SubnetKey]*bitseq.Handle) + + // Initialize address spaces + a.addrSpaces = make(map[string]*addrSpace) + for _, aspc := range []struct { + as string + ds datastore.DataStore + }{ + {localAddressSpace, lcDs}, + {globalAddressSpace, glDs}, + } { + if aspc.ds == nil { + continue + } + + a.addrSpaces[aspc.as] = &addrSpace{ + subnets: map[SubnetKey]*PoolData{}, + id: dsConfigKey + "/" + aspc.as, + scope: aspc.ds.Scope(), + ds: aspc.ds, + alloc: a, + } + } + + return a, nil +} + +func (a *Allocator) refresh(as string) error { + aSpace, err := a.getAddressSpaceFromStore(as) + if err != nil { + return fmt.Errorf("error getting pools config from store during init: %v", + err) + } + + if aSpace == nil { + return nil + } + + if err := a.updateBitMasks(aSpace); err != nil { + return fmt.Errorf("error updating bit masks during init: %v", err) + } + + a.Lock() + a.addrSpaces[as] = aSpace + a.Unlock() + + return nil +} + +func (a *Allocator) updateBitMasks(aSpace *addrSpace) error { + var inserterList []func() error + + aSpace.Lock() + for k, v := range aSpace.subnets { + if v.Range == nil { + kk := k + vv := v + inserterList = append(inserterList, func() error { return a.insertBitMask(kk, vv.Pool) }) + } + } + aSpace.Unlock() + + // Add the bitmasks (data could come from datastore) + if inserterList != nil { + for _, f := range inserterList { + if err := f(); err != nil { + return err + } + } + } + + return nil +} + +// GetDefaultAddressSpaces returns the local and global default address spaces +func (a *Allocator) GetDefaultAddressSpaces() (string, string, error) { + return localAddressSpace, globalAddressSpace, nil +} + +// RequestPool returns an address pool along with its unique id. +func (a *Allocator) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) { + log.Debugf("RequestPool(%s, %s, %s, %v, %t)", addressSpace, pool, subPool, options, v6) + k, nw, ipr, err := a.parsePoolRequest(addressSpace, pool, subPool, v6) + if err != nil { + return "", nil, nil, types.InternalErrorf("failed to parse pool request for address space %q pool %q subpool %q: %v", addressSpace, pool, subPool, err) + } + +retry: + if err := a.refresh(addressSpace); err != nil { + return "", nil, nil, err + } + + aSpace, err := a.getAddrSpace(addressSpace) + if err != nil { + return "", nil, nil, err + } + + insert, err := aSpace.updatePoolDBOnAdd(*k, nw, ipr) + if err != nil { + return "", nil, nil, err + } + + if err := a.writeToStore(aSpace); err != nil { + if _, ok := err.(types.RetryError); !ok { + return "", nil, nil, types.InternalErrorf("pool configuration failed because of %s", err.Error()) + } + + goto retry + } + + return k.String(), nw, nil, insert() +} + +// ReleasePool releases the address pool identified by the passed id +func (a *Allocator) ReleasePool(poolID string) error { + log.Debugf("ReleasePool(%s)", poolID) + k := SubnetKey{} + if err := k.FromString(poolID); err != nil { + return types.BadRequestErrorf("invalid pool id: %s", poolID) + } + +retry: + if err := a.refresh(k.AddressSpace); err != nil { + return err + } + + aSpace, err := a.getAddrSpace(k.AddressSpace) + if err != nil { + return err + } + + remove, err := aSpace.updatePoolDBOnRemoval(k) + if err != nil { + return err + } + + if err = a.writeToStore(aSpace); err != nil { + if _, ok := err.(types.RetryError); !ok { + return types.InternalErrorf("pool (%s) removal failed because of %v", poolID, err) + } + goto retry + } + + return remove() +} + +// Given the address space, returns the local or global PoolConfig based on the +// address space is local or global. AddressSpace locality is being registered with IPAM out of band. +func (a *Allocator) getAddrSpace(as string) (*addrSpace, error) { + a.Lock() + defer a.Unlock() + aSpace, ok := a.addrSpaces[as] + if !ok { + return nil, types.BadRequestErrorf("cannot find address space %s (most likey the backing datastore is not configured)", as) + } + return aSpace, nil +} + +func (a *Allocator) parsePoolRequest(addressSpace, pool, subPool string, v6 bool) (*SubnetKey, *net.IPNet, *AddressRange, error) { + var ( + nw *net.IPNet + ipr *AddressRange + err error + ) + + if addressSpace == "" { + return nil, nil, nil, ipamapi.ErrInvalidAddressSpace + } + + if pool == "" && subPool != "" { + return nil, nil, nil, ipamapi.ErrInvalidSubPool + } + + if pool != "" { + if _, nw, err = net.ParseCIDR(pool); err != nil { + return nil, nil, nil, ipamapi.ErrInvalidPool + } + if subPool != "" { + if ipr, err = getAddressRange(subPool); err != nil { + return nil, nil, nil, err + } + } + } else { + if nw, err = a.getPredefinedPool(addressSpace, v6); err != nil { + return nil, nil, nil, err + } + + } + + return &SubnetKey{AddressSpace: addressSpace, Subnet: nw.String(), ChildSubnet: subPool}, nw, ipr, nil +} + +func (a *Allocator) insertBitMask(key SubnetKey, pool *net.IPNet) error { + //log.Debugf("Inserting bitmask (%s, %s)", key.String(), pool.String()) + + store := a.getStore(key.AddressSpace) + if store == nil { + return fmt.Errorf("could not find store for address space %s while inserting bit mask", key.AddressSpace) + } + + ipVer := getAddressVersion(pool.IP) + ones, bits := pool.Mask.Size() + numAddresses := uint64(1 << uint(bits-ones)) + + if ipVer == v4 { + // Do not let broadcast address be reserved + numAddresses-- + } + + // Allow /64 subnet + if ipVer == v6 && numAddresses == 0 { + numAddresses-- + } + + // Generate the new address masks. AddressMask content may come from datastore + h, err := bitseq.NewHandle(dsDataKey, store, key.String(), numAddresses) + if err != nil { + return err + } + + // Do not let network identifier address be reserved + // Do the same for IPv6 so that bridge ip starts with XXXX...::1 + h.Set(0) + + a.Lock() + a.addresses[key] = h + a.Unlock() + return nil +} + +func (a *Allocator) retrieveBitmask(k SubnetKey, n *net.IPNet) (*bitseq.Handle, error) { + a.Lock() + bm, ok := a.addresses[k] + a.Unlock() + if !ok { + log.Debugf("Retrieving bitmask (%s, %s)", k.String(), n.String()) + if err := a.insertBitMask(k, n); err != nil { + return nil, fmt.Errorf("could not find bitmask in datastore for %s", k.String()) + } + a.Lock() + bm = a.addresses[k] + a.Unlock() + } + return bm, nil +} + +func (a *Allocator) getPredefineds(as string) []*net.IPNet { + a.Lock() + defer a.Unlock() + l := make([]*net.IPNet, 0, len(a.predefined[as])) + for _, pool := range a.predefined[as] { + l = append(l, pool) + } + return l +} + +func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error) { + var v ipVersion + v = v4 + if ipV6 { + v = v6 + } + + if as != localAddressSpace && as != globalAddressSpace { + return nil, fmt.Errorf("no default pool availbale for non-default addresss spaces") + } + + aSpace, err := a.getAddrSpace(as) + if err != nil { + return nil, err + } + + for _, nw := range a.getPredefineds(as) { + if v != getAddressVersion(nw.IP) { + continue + } + aSpace.Lock() + _, ok := aSpace.subnets[SubnetKey{AddressSpace: as, Subnet: nw.String()}] + aSpace.Unlock() + if ok { + continue + } + + if !aSpace.contains(as, nw) { + if as == localAddressSpace { + // Check if nw overlap with system routes, name servers + if _, err := ipamutils.FindAvailableNetwork([]*net.IPNet{nw}); err == nil { + return nw, nil + } + continue + } + return nw, nil + } + } + + return nil, types.NotFoundErrorf("could not find an available predefined network") +} + +// RequestAddress returns an address from the specified pool ID +func (a *Allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[string]string) (*net.IPNet, map[string]string, error) { + log.Debugf("RequestAddress(%s, %v, %v)", poolID, prefAddress, opts) + k := SubnetKey{} + if err := k.FromString(poolID); err != nil { + return nil, nil, types.BadRequestErrorf("invalid pool id: %s", poolID) + } + + if err := a.refresh(k.AddressSpace); err != nil { + return nil, nil, err + } + + aSpace, err := a.getAddrSpace(k.AddressSpace) + if err != nil { + return nil, nil, err + } + + aSpace.Lock() + p, ok := aSpace.subnets[k] + if !ok { + aSpace.Unlock() + return nil, nil, types.NotFoundErrorf("cannot find address pool for poolID:%s", poolID) + } + + if prefAddress != nil && !p.Pool.Contains(prefAddress) { + aSpace.Unlock() + return nil, nil, ipamapi.ErrIPOutOfRange + } + + c := p + for c.Range != nil { + k = c.ParentKey + c, ok = aSpace.subnets[k] + } + aSpace.Unlock() + + bm, err := a.retrieveBitmask(k, c.Pool) + if err != nil { + return nil, nil, fmt.Errorf("could not find bitmask in datastore for %s on address %v request from pool %s: %v", + k.String(), prefAddress, poolID, err) + } + ip, err := a.getAddress(p.Pool, bm, prefAddress, p.Range) + if err != nil { + return nil, nil, err + } + + return &net.IPNet{IP: ip, Mask: p.Pool.Mask}, nil, nil +} + +// ReleaseAddress releases the address from the specified pool ID +func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error { + log.Debugf("ReleaseAddress(%s, %v)", poolID, address) + k := SubnetKey{} + if err := k.FromString(poolID); err != nil { + return types.BadRequestErrorf("invalid pool id: %s", poolID) + } + + if err := a.refresh(k.AddressSpace); err != nil { + return err + } + + aSpace, err := a.getAddrSpace(k.AddressSpace) + if err != nil { + return err + } + + aSpace.Lock() + p, ok := aSpace.subnets[k] + if !ok { + aSpace.Unlock() + return ipamapi.ErrBadPool + } + + if address == nil { + aSpace.Unlock() + return ipamapi.ErrInvalidRequest + } + + if !p.Pool.Contains(address) { + aSpace.Unlock() + return ipamapi.ErrIPOutOfRange + } + + c := p + for c.Range != nil { + k = c.ParentKey + c = aSpace.subnets[k] + } + aSpace.Unlock() + + mask := p.Pool.Mask + if p.Range != nil { + mask = p.Range.Sub.Mask + } + + h, err := types.GetHostPartIP(address, mask) + if err != nil { + return fmt.Errorf("failed to release address %s: %v", address.String(), err) + } + + bm, err := a.retrieveBitmask(k, c.Pool) + if err != nil { + return fmt.Errorf("could not find bitmask in datastore for %s on address %v release from pool %s: %v", + k.String(), address, poolID, err) + } + + return bm.Unset(ipToUint64(h)) +} + +func (a *Allocator) getAddress(nw *net.IPNet, bitmask *bitseq.Handle, prefAddress net.IP, ipr *AddressRange) (net.IP, error) { + var ( + ordinal uint64 + err error + base *net.IPNet + ) + + base = types.GetIPNetCopy(nw) + + if bitmask.Unselected() <= 0 { + return nil, ipamapi.ErrNoAvailableIPs + } + if ipr == nil && prefAddress == nil { + ordinal, err = bitmask.SetAny() + } else if prefAddress != nil { + hostPart, e := types.GetHostPartIP(prefAddress, base.Mask) + if e != nil { + return nil, fmt.Errorf("failed to allocate preferred address %s: %v", prefAddress.String(), e) + } + ordinal = ipToUint64(types.GetMinimalIP(hostPart)) + err = bitmask.Set(ordinal) + } else { + base.IP = ipr.Sub.IP + ordinal, err = bitmask.SetAnyInRange(ipr.Start, ipr.End) + } + if err != nil { + return nil, ipamapi.ErrNoAvailableIPs + } + + // Convert IP ordinal for this subnet into IP address + return generateAddress(ordinal, base), nil +} + +// DumpDatabase dumps the internal info +func (a *Allocator) DumpDatabase() string { + a.Lock() + defer a.Unlock() + + var s string + for as, aSpace := range a.addrSpaces { + s = fmt.Sprintf("\n\n%s Config", as) + aSpace.Lock() + for k, config := range aSpace.subnets { + s = fmt.Sprintf("%s%s", s, fmt.Sprintf("\n%v: %v", k, config)) + } + aSpace.Unlock() + } + + s = fmt.Sprintf("%s\n\nBitmasks", s) + for k, bm := range a.addresses { + s = fmt.Sprintf("%s%s", s, fmt.Sprintf("\n\t%s: %s\n\t%d", k, bm, bm.Unselected())) + } + + return s +} diff --git a/vendor/src/github.com/docker/libnetwork/ipam/store.go b/vendor/src/github.com/docker/libnetwork/ipam/store.go new file mode 100644 index 0000000000..29cd753b60 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/ipam/store.go @@ -0,0 +1,131 @@ +package ipam + +import ( + "encoding/json" + "fmt" + + log "github.com/Sirupsen/logrus" + "github.com/docker/libnetwork/datastore" + "github.com/docker/libnetwork/types" +) + +// Key provides the Key to be used in KV Store +func (aSpace *addrSpace) Key() []string { + aSpace.Lock() + defer aSpace.Unlock() + return []string{aSpace.id} +} + +// KeyPrefix returns the immediate parent key that can be used for tree walk +func (aSpace *addrSpace) KeyPrefix() []string { + aSpace.Lock() + defer aSpace.Unlock() + return []string{dsConfigKey} +} + +// Value marshals the data to be stored in the KV store +func (aSpace *addrSpace) Value() []byte { + b, err := json.Marshal(aSpace) + if err != nil { + log.Warnf("Failed to marshal ipam configured pools: %v", err) + return nil + } + return b +} + +// SetValue unmarshalls the data from the KV store. +func (aSpace *addrSpace) SetValue(value []byte) error { + rc := &addrSpace{subnets: make(map[SubnetKey]*PoolData)} + if err := json.Unmarshal(value, rc); err != nil { + return err + } + aSpace.subnets = rc.subnets + return nil +} + +// Index returns the latest DB Index as seen by this object +func (aSpace *addrSpace) Index() uint64 { + aSpace.Lock() + defer aSpace.Unlock() + return aSpace.dbIndex +} + +// SetIndex method allows the datastore to store the latest DB Index into this object +func (aSpace *addrSpace) SetIndex(index uint64) { + aSpace.Lock() + aSpace.dbIndex = index + aSpace.dbExists = true + aSpace.Unlock() +} + +// Exists method is true if this object has been stored in the DB. +func (aSpace *addrSpace) Exists() bool { + aSpace.Lock() + defer aSpace.Unlock() + return aSpace.dbExists +} + +// Skip provides a way for a KV Object to avoid persisting it in the KV Store +func (aSpace *addrSpace) Skip() bool { + return false +} + +func (a *Allocator) getStore(as string) datastore.DataStore { + a.Lock() + defer a.Unlock() + + if aSpace, ok := a.addrSpaces[as]; ok { + return aSpace.ds + } + + return nil +} + +func (a *Allocator) getAddressSpaceFromStore(as string) (*addrSpace, error) { + store := a.getStore(as) + if store == nil { + return nil, fmt.Errorf("store for address space %s not found", as) + } + + pc := &addrSpace{id: dsConfigKey + "/" + as, ds: store, alloc: a} + if err := store.GetObject(datastore.Key(pc.Key()...), pc); err != nil { + if err == datastore.ErrKeyNotFound { + return nil, nil + } + + return nil, fmt.Errorf("could not get pools config from store: %v", err) + } + + return pc, nil +} + +func (a *Allocator) writeToStore(aSpace *addrSpace) error { + store := aSpace.store() + if store == nil { + return fmt.Errorf("invalid store while trying to write %s address space", aSpace.DataScope()) + } + + err := store.PutObjectAtomic(aSpace) + if err == datastore.ErrKeyModified { + return types.RetryErrorf("failed to perform atomic write (%v). retry might fix the error", err) + } + + return err +} + +func (a *Allocator) deleteFromStore(aSpace *addrSpace) error { + store := aSpace.store() + if store == nil { + return fmt.Errorf("invalid store while trying to delete %s address space", aSpace.DataScope()) + } + + return store.DeleteObjectAtomic(aSpace) +} + +// DataScope method returns the storage scope of the datastore +func (aSpace *addrSpace) DataScope() string { + aSpace.Lock() + defer aSpace.Unlock() + + return aSpace.scope +} diff --git a/vendor/src/github.com/docker/libnetwork/ipam/structures.go b/vendor/src/github.com/docker/libnetwork/ipam/structures.go new file mode 100644 index 0000000000..9ac171887c --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/ipam/structures.go @@ -0,0 +1,359 @@ +package ipam + +import ( + "encoding/json" + "fmt" + "net" + "strings" + "sync" + + "github.com/docker/libnetwork/datastore" + "github.com/docker/libnetwork/ipamapi" + "github.com/docker/libnetwork/types" +) + +// SubnetKey is the pointer to the configured pools in each address space +type SubnetKey struct { + AddressSpace string + Subnet string + ChildSubnet string +} + +// PoolData contains the configured pool data +type PoolData struct { + ParentKey SubnetKey + Pool *net.IPNet + Range *AddressRange `json:",omitempty"` + RefCount int +} + +// addrSpace contains the pool configurations for the address space +type addrSpace struct { + subnets map[SubnetKey]*PoolData + dbIndex uint64 + dbExists bool + id string + scope string + ds datastore.DataStore + alloc *Allocator + sync.Mutex +} + +// AddressRange specifies first and last ip ordinal which +// identify a range in a a pool of addresses +type AddressRange struct { + Sub *net.IPNet + Start, End uint64 +} + +// String returns the string form of the AddressRange object +func (r *AddressRange) String() string { + return fmt.Sprintf("Sub: %s, range [%d, %d]", r.Sub, r.Start, r.End) +} + +// MarshalJSON returns the JSON encoding of the Range object +func (r *AddressRange) MarshalJSON() ([]byte, error) { + m := map[string]interface{}{ + "Sub": r.Sub.String(), + "Start": r.Start, + "End": r.End, + } + return json.Marshal(m) +} + +// UnmarshalJSON decodes data into the Range object +func (r *AddressRange) UnmarshalJSON(data []byte) error { + m := map[string]interface{}{} + err := json.Unmarshal(data, &m) + if err != nil { + return err + } + if r.Sub, err = types.ParseCIDR(m["Sub"].(string)); err != nil { + return err + } + r.Start = uint64(m["Start"].(float64)) + r.End = uint64(m["End"].(float64)) + return nil +} + +// String returns the string form of the SubnetKey object +func (s *SubnetKey) String() string { + k := fmt.Sprintf("%s/%s", s.AddressSpace, s.Subnet) + if s.ChildSubnet != "" { + k = fmt.Sprintf("%s/%s", k, s.ChildSubnet) + } + return k +} + +// FromString populate the SubnetKey object reading it from string +func (s *SubnetKey) FromString(str string) error { + if str == "" || !strings.Contains(str, "/") { + return fmt.Errorf("invalid string form for subnetkey: %s", str) + } + + p := strings.Split(str, "/") + if len(p) != 3 && len(p) != 5 { + return fmt.Errorf("invalid string form for subnetkey: %s", str) + } + s.AddressSpace = p[0] + s.Subnet = fmt.Sprintf("%s/%s", p[1], p[2]) + if len(p) == 5 { + s.ChildSubnet = fmt.Sprintf("%s/%s", p[3], p[4]) + } + + return nil +} + +// String returns the string form of the PoolData object +func (p *PoolData) String() string { + return fmt.Sprintf("ParentKey: %s, Pool: %s, Range: %s, RefCount: %d", + p.ParentKey.String(), p.Pool.String(), p.Range, p.RefCount) +} + +// MarshalJSON returns the JSON encoding of the PoolData object +func (p *PoolData) MarshalJSON() ([]byte, error) { + m := map[string]interface{}{ + "ParentKey": p.ParentKey, + "RefCount": p.RefCount, + } + if p.Pool != nil { + m["Pool"] = p.Pool.String() + } + if p.Range != nil { + m["Range"] = p.Range + } + return json.Marshal(m) +} + +// UnmarshalJSON decodes data into the PoolData object +func (p *PoolData) UnmarshalJSON(data []byte) error { + var ( + err error + t struct { + ParentKey SubnetKey + Pool string + Range *AddressRange `json:",omitempty"` + RefCount int + } + ) + + if err = json.Unmarshal(data, &t); err != nil { + return err + } + + p.ParentKey = t.ParentKey + p.Range = t.Range + p.RefCount = t.RefCount + if t.Pool != "" { + if p.Pool, err = types.ParseCIDR(t.Pool); err != nil { + return err + } + } + + return nil +} + +// MarshalJSON returns the JSON encoding of the addrSpace object +func (aSpace *addrSpace) MarshalJSON() ([]byte, error) { + aSpace.Lock() + defer aSpace.Unlock() + + m := map[string]interface{}{ + "Scope": string(aSpace.scope), + } + + if aSpace.subnets != nil { + s := map[string]*PoolData{} + for k, v := range aSpace.subnets { + s[k.String()] = v + } + m["Subnets"] = s + } + + return json.Marshal(m) +} + +// UnmarshalJSON decodes data into the addrSpace object +func (aSpace *addrSpace) UnmarshalJSON(data []byte) error { + aSpace.Lock() + defer aSpace.Unlock() + + m := map[string]interface{}{} + err := json.Unmarshal(data, &m) + if err != nil { + return err + } + + aSpace.scope = datastore.LocalScope + s := m["Scope"].(string) + if s == string(datastore.GlobalScope) { + aSpace.scope = datastore.GlobalScope + } + + if v, ok := m["Subnets"]; ok { + sb, _ := json.Marshal(v) + var s map[string]*PoolData + err := json.Unmarshal(sb, &s) + if err != nil { + return err + } + for ks, v := range s { + k := SubnetKey{} + k.FromString(ks) + aSpace.subnets[k] = v + } + } + + return nil +} + +// CopyTo deep copies the pool data to the destination pooldata +func (p *PoolData) CopyTo(dstP *PoolData) error { + dstP.ParentKey = p.ParentKey + dstP.Pool = types.GetIPNetCopy(p.Pool) + + if p.Range != nil { + dstP.Range = &AddressRange{} + dstP.Range.Sub = types.GetIPNetCopy(p.Range.Sub) + dstP.Range.Start = p.Range.Start + dstP.Range.End = p.Range.End + } + + dstP.RefCount = p.RefCount + return nil +} + +func (aSpace *addrSpace) CopyTo(o datastore.KVObject) error { + aSpace.Lock() + defer aSpace.Unlock() + + dstAspace := o.(*addrSpace) + + dstAspace.id = aSpace.id + dstAspace.ds = aSpace.ds + dstAspace.alloc = aSpace.alloc + dstAspace.scope = aSpace.scope + dstAspace.dbIndex = aSpace.dbIndex + dstAspace.dbExists = aSpace.dbExists + + dstAspace.subnets = make(map[SubnetKey]*PoolData) + for k, v := range aSpace.subnets { + dstAspace.subnets[k] = &PoolData{} + v.CopyTo(dstAspace.subnets[k]) + } + + return nil +} + +func (aSpace *addrSpace) New() datastore.KVObject { + aSpace.Lock() + defer aSpace.Unlock() + + return &addrSpace{ + id: aSpace.id, + ds: aSpace.ds, + alloc: aSpace.alloc, + scope: aSpace.scope, + } +} + +func (aSpace *addrSpace) updatePoolDBOnAdd(k SubnetKey, nw *net.IPNet, ipr *AddressRange) (func() error, error) { + aSpace.Lock() + defer aSpace.Unlock() + + // Check if already allocated + if p, ok := aSpace.subnets[k]; ok { + aSpace.incRefCount(p, 1) + return func() error { return nil }, nil + } + + // If master pool, check for overlap + if ipr == nil { + if aSpace.contains(k.AddressSpace, nw) { + return nil, ipamapi.ErrPoolOverlap + } + // This is a new master pool, add it along with corresponding bitmask + aSpace.subnets[k] = &PoolData{Pool: nw, RefCount: 1} + return func() error { return aSpace.alloc.insertBitMask(k, nw) }, nil + } + + // This is a new non-master pool + p := &PoolData{ + ParentKey: SubnetKey{AddressSpace: k.AddressSpace, Subnet: k.Subnet}, + Pool: nw, + Range: ipr, + RefCount: 1, + } + aSpace.subnets[k] = p + + // Look for parent pool + pp, ok := aSpace.subnets[p.ParentKey] + if ok { + aSpace.incRefCount(pp, 1) + return func() error { return nil }, nil + } + + // Parent pool does not exist, add it along with corresponding bitmask + aSpace.subnets[p.ParentKey] = &PoolData{Pool: nw, RefCount: 1} + return func() error { return aSpace.alloc.insertBitMask(p.ParentKey, nw) }, nil +} + +func (aSpace *addrSpace) updatePoolDBOnRemoval(k SubnetKey) (func() error, error) { + aSpace.Lock() + defer aSpace.Unlock() + + p, ok := aSpace.subnets[k] + if !ok { + return nil, ipamapi.ErrBadPool + } + + aSpace.incRefCount(p, -1) + + c := p + for ok { + if c.RefCount == 0 { + delete(aSpace.subnets, k) + if c.Range == nil { + return func() error { + bm, err := aSpace.alloc.retrieveBitmask(k, c.Pool) + if err != nil { + return fmt.Errorf("could not find bitmask in datastore for pool %s removal: %v", k.String(), err) + } + return bm.Destroy() + }, nil + } + } + k = c.ParentKey + c, ok = aSpace.subnets[k] + } + + return func() error { return nil }, nil +} + +func (aSpace *addrSpace) incRefCount(p *PoolData, delta int) { + c := p + ok := true + for ok { + c.RefCount += delta + c, ok = aSpace.subnets[c.ParentKey] + } +} + +// Checks whether the passed subnet is a superset or subset of any of the subset in this config db +func (aSpace *addrSpace) contains(space string, nw *net.IPNet) bool { + for k, v := range aSpace.subnets { + if space == k.AddressSpace && k.ChildSubnet == "" { + if nw.Contains(v.Pool.IP) || v.Pool.Contains(nw.IP) { + return true + } + } + } + return false +} + +func (aSpace *addrSpace) store() datastore.DataStore { + aSpace.Lock() + defer aSpace.Unlock() + + return aSpace.ds +} diff --git a/vendor/src/github.com/docker/libnetwork/ipam/utils.go b/vendor/src/github.com/docker/libnetwork/ipam/utils.go new file mode 100644 index 0000000000..2d9c6b17bf --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/ipam/utils.go @@ -0,0 +1,81 @@ +package ipam + +import ( + "fmt" + "net" + + "github.com/docker/libnetwork/ipamapi" + "github.com/docker/libnetwork/types" +) + +type ipVersion int + +const ( + v4 = 4 + v6 = 6 +) + +func getAddressRange(pool string) (*AddressRange, error) { + ip, nw, err := net.ParseCIDR(pool) + if err != nil { + return nil, ipamapi.ErrInvalidSubPool + } + lIP, e := types.GetHostPartIP(nw.IP, nw.Mask) + if e != nil { + return nil, fmt.Errorf("failed to compute range's lowest ip address: %v", e) + } + bIP, e := types.GetBroadcastIP(nw.IP, nw.Mask) + if e != nil { + return nil, fmt.Errorf("failed to compute range's broadcast ip address: %v", e) + } + hIP, e := types.GetHostPartIP(bIP, nw.Mask) + if e != nil { + return nil, fmt.Errorf("failed to compute range's highest ip address: %v", e) + } + nw.IP = ip + return &AddressRange{nw, ipToUint64(types.GetMinimalIP(lIP)), ipToUint64(types.GetMinimalIP(hIP))}, nil +} + +// It generates the ip address in the passed subnet specified by +// the passed host address ordinal +func generateAddress(ordinal uint64, network *net.IPNet) net.IP { + var address [16]byte + + // Get network portion of IP + if getAddressVersion(network.IP) == v4 { + copy(address[:], network.IP.To4()) + } else { + copy(address[:], network.IP) + } + + end := len(network.Mask) + addIntToIP(address[:end], ordinal) + + return net.IP(address[:end]) +} + +func getAddressVersion(ip net.IP) ipVersion { + if ip.To4() == nil { + return v6 + } + return v4 +} + +// Adds the ordinal IP to the current array +// 192.168.0.0 + 53 => 192.168.53 +func addIntToIP(array []byte, ordinal uint64) { + for i := len(array) - 1; i >= 0; i-- { + array[i] |= (byte)(ordinal & 0xff) + ordinal >>= 8 + } +} + +// Convert an ordinal to the respective IP address +func ipToUint64(ip []byte) (value uint64) { + cip := types.GetMinimalIP(ip) + for i := 0; i < len(cip); i++ { + j := len(cip) - 1 - i + value += uint64(cip[i]) << uint(j*8) + } + return value +} diff --git a/vendor/src/github.com/docker/libnetwork/ipamapi/contract.go b/vendor/src/github.com/docker/libnetwork/ipamapi/contract.go new file mode 100644 index 0000000000..2b11b303a4 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/ipamapi/contract.go @@ -0,0 +1,72 @@ +// Package ipamapi specifies the contract the IPAM service (built-in or remote) needs to satisfy. +package ipamapi + +import ( + "errors" + "net" +) + +/******************** + * IPAM plugin types + ********************/ + +const ( + // DefaultIPAM is the name of the built-in default ipam driver + DefaultIPAM = "default" + // PluginEndpointType represents the Endpoint Type used by Plugin system + PluginEndpointType = "IpamDriver" +) + +// Callback provides a Callback interface for registering an IPAM instance into LibNetwork +type Callback interface { + // RegisterDriver provides a way for Remote drivers to dynamically register new NetworkType and associate with a ipam instance + RegisterIpamDriver(name string, driver Ipam) error +} + +/************** + * IPAM Errors + **************/ + +// Weel-known errors returned by IPAM +var ( + ErrInvalidIpamService = errors.New("Invalid IPAM Service") + ErrInvalidIpamConfigService = errors.New("Invalid IPAM Config Service") + ErrIpamNotAvailable = errors.New("IPAM Service not available") + ErrIpamInternalError = errors.New("IPAM Internal Error") + ErrInvalidAddressSpace = errors.New("Invalid Address Space") + ErrInvalidPool = errors.New("Invalid Address Pool") + ErrInvalidSubPool = errors.New("Invalid Address SubPool") + ErrInvalidRequest = errors.New("Invalid Request") + ErrPoolNotFound = errors.New("Address Pool not found") + ErrOverlapPool = errors.New("Address pool overlaps with existing pool on this address space") + ErrNoAvailablePool = errors.New("No available pool") + ErrNoAvailableIPs = errors.New("No available addresses on this pool") + ErrIPAlreadyAllocated = errors.New("Address already in use") + ErrIPOutOfRange = errors.New("Requested address is out of range") + ErrPoolOverlap = errors.New("Pool overlaps with other one on this address space") + ErrBadPool = errors.New("Address space does not contain specified address pool") +) + +/******************************* + * IPAM Service Interface + *******************************/ + +// Ipam represents the interface the IPAM service plugins must implement +// in order to allow injection/modification of IPAM database. +type Ipam interface { + // GetDefaultAddressSpaces returns the default local and global address spaces for this ipam + GetDefaultAddressSpaces() (string, string, error) + // RequestPool returns an address pool along with its unique id. Address space is a mandatory field + // which denotes a set of non-overlapping pools. pool describes the pool of addresses in CIDR notation. + // subpool indicates a smaller range of addresses from the pool, for now it is specified in CIDR notation. + // Both pool and subpool are non mandatory fields. When they are not specified, Ipam driver may choose to + // return a self chosen pool for this request. In such case the v6 flag needs to be set appropriately so + // that the driver would return the expected ip version pool. + RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) + // ReleasePool releases the address pool identified by the passed id + ReleasePool(poolID string) error + // Request address from the specified pool ID. Input options or preferred IP can be passed. + RequestAddress(string, net.IP, map[string]string) (*net.IPNet, map[string]string, error) + // Release the address from the specified pool ID + ReleaseAddress(string, net.IP) error +} diff --git a/vendor/src/github.com/docker/libnetwork/ipams/builtin/builtin.go b/vendor/src/github.com/docker/libnetwork/ipams/builtin/builtin.go new file mode 100644 index 0000000000..707e001f73 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/ipams/builtin/builtin.go @@ -0,0 +1,35 @@ +package builtin + +import ( + "fmt" + + "github.com/docker/libnetwork/datastore" + "github.com/docker/libnetwork/ipam" + "github.com/docker/libnetwork/ipamapi" +) + +// Init registers the built-in ipam service with libnetwork +func Init(ic ipamapi.Callback, l, g interface{}) error { + var ( + ok bool + localDs, globalDs datastore.DataStore + ) + + if l != nil { + if localDs, ok = l.(datastore.DataStore); !ok { + return fmt.Errorf("incorrect local datastore passed to built-in ipam init") + } + } + + if g != nil { + if globalDs, ok = g.(datastore.DataStore); !ok { + return fmt.Errorf("incorrect global datastore passed to built-in ipam init") + } + } + a, err := ipam.NewAllocator(localDs, globalDs) + if err != nil { + return err + } + + return ic.RegisterIpamDriver(ipamapi.DefaultIPAM, a) +} diff --git a/vendor/src/github.com/docker/libnetwork/ipams/remote/api/api.go b/vendor/src/github.com/docker/libnetwork/ipams/remote/api/api.go new file mode 100644 index 0000000000..b8d21fdc6b --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/ipams/remote/api/api.go @@ -0,0 +1,77 @@ +// Package api defines the data structure to be used in the request/response +// messages between libnetwork and the remote ipam plugin +package api + +// Response is the basic response structure used in all responses +type Response struct { + Error string +} + +// IsSuccess returns wheter the plugin response is successful +func (r *Response) IsSuccess() bool { + return r.Error == "" +} + +// GetError returns the error from the response, if any. +func (r *Response) GetError() string { + return r.Error +} + +// GetAddressSpacesResponse is the response to the ``get default address spaces`` request message +type GetAddressSpacesResponse struct { + Response + LocalDefaultAddressSpace string + GlobalDefaultAddressSpace string +} + +// RequestPoolRequest represents the expected data in a ``request address pool`` request message +type RequestPoolRequest struct { + AddressSpace string + Pool string + SubPool string + Options map[string]string + V6 bool +} + +// RequestPoolResponse represents the response message to a ``request address pool`` request +type RequestPoolResponse struct { + Response + PoolID string + Pool string // CIDR format + Data map[string]string +} + +// ReleasePoolRequest represents the expected data in a ``release address pool`` request message +type ReleasePoolRequest struct { + PoolID string +} + +// ReleasePoolResponse represents the response message to a ``release address pool`` request +type ReleasePoolResponse struct { + Response +} + +// RequestAddressRequest represents the expected data in a ``request address`` request message +type RequestAddressRequest struct { + PoolID string + Address string + Options map[string]string +} + +// RequestAddressResponse represents the expected data in the response message to a ``request address`` request +type RequestAddressResponse struct { + Response + Address string // in CIDR format + Data map[string]string +} + +// ReleaseAddressRequest represents the expected data in a ``release address`` request message +type ReleaseAddressRequest struct { + PoolID string + Address string +} + +// ReleaseAddressResponse represents the response message to a ``release address`` request +type ReleaseAddressResponse struct { + Response +} diff --git a/vendor/src/github.com/docker/libnetwork/ipams/remote/remote.go b/vendor/src/github.com/docker/libnetwork/ipams/remote/remote.go new file mode 100644 index 0000000000..f9df525ced --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/ipams/remote/remote.go @@ -0,0 +1,103 @@ +package remote + +import ( + "fmt" + "net" + + log "github.com/Sirupsen/logrus" + "github.com/docker/docker/pkg/plugins" + "github.com/docker/libnetwork/ipamapi" + "github.com/docker/libnetwork/ipams/remote/api" + "github.com/docker/libnetwork/types" +) + +type allocator struct { + endpoint *plugins.Client + name string +} + +// PluginResponse is the interface for the plugin request responses +type PluginResponse interface { + IsSuccess() bool + GetError() string +} + +func newAllocator(name string, client *plugins.Client) ipamapi.Ipam { + a := &allocator{name: name, endpoint: client} + return a +} + +// Init registers a remote ipam when its plugin is activated +func Init(cb ipamapi.Callback, l, g interface{}) error { + plugins.Handle(ipamapi.PluginEndpointType, func(name string, client *plugins.Client) { + if err := cb.RegisterIpamDriver(name, newAllocator(name, client)); err != nil { + log.Errorf("error registering remote ipam %s due to %v", name, err) + } + }) + return nil +} + +func (a *allocator) call(methodName string, arg interface{}, retVal PluginResponse) error { + method := ipamapi.PluginEndpointType + "." + methodName + err := a.endpoint.Call(method, arg, retVal) + if err != nil { + return err + } + if !retVal.IsSuccess() { + return fmt.Errorf("remote: %s", retVal.GetError()) + } + return nil +} + +// GetDefaultAddressSpaces returns the local and global default address spaces +func (a *allocator) GetDefaultAddressSpaces() (string, string, error) { + res := &api.GetAddressSpacesResponse{} + if err := a.call("GetDefaultAddressSpaces", nil, res); err != nil { + return "", "", err + } + return res.LocalDefaultAddressSpace, res.GlobalDefaultAddressSpace, nil +} + +// RequestPool requests an address pool in the specified address space +func (a *allocator) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) { + req := &api.RequestPoolRequest{AddressSpace: addressSpace, Pool: pool, SubPool: subPool, Options: options, V6: v6} + res := &api.RequestPoolResponse{} + if err := a.call("RequestPool", req, res); err != nil { + return "", nil, nil, err + } + retPool, err := types.ParseCIDR(res.Pool) + return res.PoolID, retPool, res.Data, err +} + +// ReleasePool removes an address pool from the specified address space +func (a *allocator) ReleasePool(poolID string) error { + req := &api.ReleasePoolRequest{PoolID: poolID} + res := &api.ReleasePoolResponse{} + return a.call("ReleasePool", req, res) +} + +// RequestAddress requests an address from the address pool +func (a *allocator) RequestAddress(poolID string, address net.IP, options map[string]string) (*net.IPNet, map[string]string, error) { + var prefAddress string + if address != nil { + prefAddress = address.String() + } + req := &api.RequestAddressRequest{PoolID: poolID, Address: prefAddress, Options: options} + res := &api.RequestAddressResponse{} + if err := a.call("RequestAddress", req, res); err != nil { + return nil, nil, err + } + retAddress, err := types.ParseCIDR(res.Address) + return retAddress, res.Data, err +} + +// ReleaseAddress releases the address from the specified address pool +func (a *allocator) ReleaseAddress(poolID string, address net.IP) error { + var relAddress string + if address != nil { + relAddress = address.String() + } + req := &api.ReleaseAddressRequest{PoolID: poolID, Address: relAddress} + res := &api.ReleaseAddressResponse{} + return a.call("ReleaseAddress", req, res) +} diff --git a/vendor/src/github.com/docker/libnetwork/ipamutils/utils.go b/vendor/src/github.com/docker/libnetwork/ipamutils/utils.go new file mode 100644 index 0000000000..283e003b5c --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/ipamutils/utils.go @@ -0,0 +1,42 @@ +// Package ipamutils provides utililty functions for ipam management +package ipamutils + +import "net" + +var ( + // PredefinedBroadNetworks contains a list of 31 IPv4 private networks with host size 16 and 12 + // (172.17-31.x.x/16, 192.168.x.x/20) which do not overlap with the networks in `PredefinedGranularNetworks` + PredefinedBroadNetworks []*net.IPNet + // PredefinedGranularNetworks contains a list of 64K IPv4 private networks with host size 8 + // (10.x.x.x/24) which do not overlap with the networks in `PredefinedBroadNetworks` + PredefinedGranularNetworks []*net.IPNet +) + +func init() { + PredefinedBroadNetworks = initBroadPredefinedNetworks() + PredefinedGranularNetworks = initGranularPredefinedNetworks() +} + +func initBroadPredefinedNetworks() []*net.IPNet { + pl := make([]*net.IPNet, 0, 31) + mask := []byte{255, 255, 0, 0} + for i := 17; i < 32; i++ { + pl = append(pl, &net.IPNet{IP: []byte{172, byte(i), 0, 0}, Mask: mask}) + } + mask20 := []byte{255, 255, 240, 0} + for i := 0; i < 16; i++ { + pl = append(pl, &net.IPNet{IP: []byte{192, 168, byte(i << 4), 0}, Mask: mask20}) + } + return pl +} + +func initGranularPredefinedNetworks() []*net.IPNet { + pl := make([]*net.IPNet, 0, 256*256) + mask := []byte{255, 255, 255, 0} + for i := 0; i < 256; i++ { + for j := 0; j < 256; j++ { + pl = append(pl, &net.IPNet{IP: []byte{10, byte(i), byte(j), 0}, Mask: mask}) + } + } + return pl +} diff --git a/vendor/src/github.com/docker/libnetwork/ipamutils/utils_linux.go b/vendor/src/github.com/docker/libnetwork/ipamutils/utils_linux.go new file mode 100644 index 0000000000..d8c9eb8a1c --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/ipamutils/utils_linux.go @@ -0,0 +1,71 @@ +// Package ipamutils provides utililty functions for ipam management +package ipamutils + +import ( + "fmt" + "net" + + "github.com/docker/libnetwork/netutils" + "github.com/docker/libnetwork/resolvconf" + "github.com/vishvananda/netlink" +) + +// ElectInterfaceAddresses looks for an interface on the OS with the specified name +// and returns its IPv4 and IPv6 addresses in CIDR form. If the interface does not exist, +// it chooses from a predifined list the first IPv4 address which does not conflict +// with other interfaces on the system. +func ElectInterfaceAddresses(name string) (*net.IPNet, []*net.IPNet, error) { + var ( + v4Net *net.IPNet + v6Nets []*net.IPNet + err error + ) + + link, _ := netlink.LinkByName(name) + if link != nil { + v4addr, err := netlink.AddrList(link, netlink.FAMILY_V4) + if err != nil { + return nil, nil, err + } + v6addr, err := netlink.AddrList(link, netlink.FAMILY_V6) + if err != nil { + return nil, nil, err + } + if len(v4addr) > 0 { + v4Net = v4addr[0].IPNet + } + for _, nlAddr := range v6addr { + v6Nets = append(v6Nets, nlAddr.IPNet) + } + } + + if link == nil || v4Net == nil { + // Choose from predifined broad networks + v4Net, err = FindAvailableNetwork(PredefinedBroadNetworks) + if err != nil { + return nil, nil, err + } + } + + return v4Net, v6Nets, nil +} + +// FindAvailableNetwork returns a network from the passed list which does not +// overlap with existing interfaces in the system +func FindAvailableNetwork(list []*net.IPNet) (*net.IPNet, error) { + // We don't check for an error here, because we don't really care if we + // can't read /etc/resolv.conf. So instead we skip the append if resolvConf + // is nil. It either doesn't exist, or we can't read it for some reason. + var nameservers []string + if rc, err := resolvconf.Get(); err == nil { + nameservers = resolvconf.GetNameserversAsCIDR(rc.Content) + } + for _, nw := range list { + if err := netutils.CheckNameserverOverlaps(nameservers, nw); err == nil { + if err := netutils.CheckRouteOverlaps(nw); err == nil { + return nw, nil + } + } + } + return nil, fmt.Errorf("no available network") +} diff --git a/vendor/src/github.com/docker/libnetwork/ipamutils/utils_windows.go b/vendor/src/github.com/docker/libnetwork/ipamutils/utils_windows.go new file mode 100644 index 0000000000..4878ca2b86 --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/ipamutils/utils_windows.go @@ -0,0 +1,22 @@ +// Package ipamutils provides utililty functions for ipam management +package ipamutils + +import ( + "net" + + "github.com/docker/libnetwork/types" +) + +// ElectInterfaceAddresses looks for an interface on the OS with the specified name +// and returns its IPv4 and IPv6 addresses in CIDR form. If the interface does not exist, +// it chooses from a predifined list the first IPv4 address which does not conflict +// with other interfaces on the system. +func ElectInterfaceAddresses(name string) (*net.IPNet, []*net.IPNet, error) { + return nil, nil, types.NotImplementedErrorf("not supported on windows") +} + +// FindAvailableNetwork returns a network from the passed list which does not +// overlap with existing interfaces in the system +func FindAvailableNetwork(list []*net.IPNet) (*net.IPNet, error) { + return nil, types.NotImplementedErrorf("not supported on windows") +} diff --git a/vendor/src/github.com/docker/libnetwork/netlabel/labels.go b/vendor/src/github.com/docker/libnetwork/netlabel/labels.go index d2db2403ed..cb0c2f5402 100644 --- a/vendor/src/github.com/docker/libnetwork/netlabel/labels.go +++ b/vendor/src/github.com/docker/libnetwork/netlabel/labels.go @@ -1,6 +1,8 @@ package netlabel -import "strings" +import ( + "strings" +) const ( // Prefix constant marks the reserved label space for libnetwork @@ -9,6 +11,10 @@ const ( // DriverPrefix constant marks the reserved label space for libnetwork drivers DriverPrefix = Prefix + ".driver" + // DriverPrivatePrefix constant marks the reserved label space + // for internal libnetwork drivers + DriverPrivatePrefix = DriverPrefix + ".private" + // GenericData constant that helps to identify an option as a Generic constant GenericData = Prefix + ".generic" @@ -18,38 +24,83 @@ const ( // MacAddress constant represents Mac Address config of a Container MacAddress = Prefix + ".endpoint.macaddress" - // ExposedPorts constant represents exposedports of a Container + // ExposedPorts constant represents the container's Exposed Ports ExposedPorts = Prefix + ".endpoint.exposedports" //EnableIPv6 constant represents enabling IPV6 at network level EnableIPv6 = Prefix + ".enable_ipv6" - // KVProvider constant represents the KV provider backend - KVProvider = DriverPrefix + ".kv_provider" - - // KVProviderURL constant represents the KV provider URL - KVProviderURL = DriverPrefix + ".kv_provider_url" - - // KVProviderConfig constant represents the KV provider Config - KVProviderConfig = DriverPrefix + ".kv_provider_config" + // DriverMTU constant represents the MTU size for the network driver + DriverMTU = DriverPrefix + ".mtu" // OverlayBindInterface constant represents overlay driver bind interface OverlayBindInterface = DriverPrefix + ".overlay.bind_interface" // OverlayNeighborIP constant represents overlay driver neighbor IP OverlayNeighborIP = DriverPrefix + ".overlay.neighbor_ip" + + // Gateway represents the gateway for the network + Gateway = Prefix + ".gateway" ) -// Key extracts the key portion of the label -func Key(label string) string { - kv := strings.SplitN(label, "=", 2) +var ( + // GlobalKVProvider constant represents the KV provider backend + GlobalKVProvider = MakeKVProvider("global") - return kv[0] + // GlobalKVProviderURL constant represents the KV provider URL + GlobalKVProviderURL = MakeKVProviderURL("global") + + // GlobalKVProviderConfig constant represents the KV provider Config + GlobalKVProviderConfig = MakeKVProviderConfig("global") + + // LocalKVProvider constant represents the KV provider backend + LocalKVProvider = MakeKVProvider("local") + + // LocalKVProviderURL constant represents the KV provider URL + LocalKVProviderURL = MakeKVProviderURL("local") + + // LocalKVProviderConfig constant represents the KV provider Config + LocalKVProviderConfig = MakeKVProviderConfig("local") +) + +// MakeKVProvider returns the kvprovider label for the scope +func MakeKVProvider(scope string) string { + return DriverPrivatePrefix + scope + "kv_provider" +} + +// MakeKVProviderURL returns the kvprovider url label for the scope +func MakeKVProviderURL(scope string) string { + return DriverPrivatePrefix + scope + "kv_provider_url" +} + +// MakeKVProviderConfig returns the kvprovider config label for the scope +func MakeKVProviderConfig(scope string) string { + return DriverPrivatePrefix + scope + "kv_provider_config" +} + +// Key extracts the key portion of the label +func Key(label string) (key string) { + if kv := strings.SplitN(label, "=", 2); len(kv) > 0 { + key = kv[0] + } + return } // Value extracts the value portion of the label -func Value(label string) string { - kv := strings.SplitN(label, "=", 2) - - return kv[1] +func Value(label string) (value string) { + if kv := strings.SplitN(label, "=", 2); len(kv) > 1 { + value = kv[1] + } + return +} + +// KeyValue decomposes the label in the (key,value) pair +func KeyValue(label string) (key string, value string) { + if kv := strings.SplitN(label, "=", 2); len(kv) > 0 { + key = kv[0] + if len(kv) > 1 { + value = kv[1] + } + } + return } diff --git a/vendor/src/github.com/docker/libnetwork/network.go b/vendor/src/github.com/docker/libnetwork/network.go index ea7d4ea600..b9a2d5a1b9 100644 --- a/vendor/src/github.com/docker/libnetwork/network.go +++ b/vendor/src/github.com/docker/libnetwork/network.go @@ -2,7 +2,9 @@ package libnetwork import ( "encoding/json" + "fmt" "net" + "strconv" "sync" log "github.com/Sirupsen/logrus" @@ -11,6 +13,7 @@ import ( "github.com/docker/libnetwork/datastore" "github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/etchosts" + "github.com/docker/libnetwork/ipamapi" "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/options" "github.com/docker/libnetwork/types" @@ -47,6 +50,16 @@ type Network interface { // EndpointByID returns the Endpoint which has the passed id. If not found, the error ErrNoSuchEndpoint is returned. EndpointByID(id string) (Endpoint, error) + + // Return certain operational data belonging to this network + Info() NetworkInfo +} + +// NetworkInfo returns some configuration and operational information about the network +type NetworkInfo interface { + IpamConfig() (string, []*IpamConf, []*IpamConf) + Labels() map[string]string + Scope() string } // EndpointWalker is a client provided function which will be used to walk the Endpoints. @@ -55,22 +68,98 @@ type EndpointWalker func(ep Endpoint) bool type svcMap map[string]net.IP +// IpamConf contains all the ipam related configurations for a network +type IpamConf struct { + // The master address pool for containers and network interfaces + PreferredPool string + // A subset of the master pool. If specified, + // this becomes the container pool + SubPool string + // Input options for IPAM Driver (optional) + Options map[string]string + // Preferred Network Gateway address (optional) + Gateway string + // Auxiliary addresses for network driver. Must be within the master pool. + // libnetwork will reserve them if they fall into the container pool + AuxAddresses map[string]string +} + +// Validate checks whether the configuration is valid +func (c *IpamConf) Validate() error { + if c.Gateway != "" && nil == net.ParseIP(c.Gateway) { + return types.BadRequestErrorf("invalid gateway address %s in Ipam configuration", c.Gateway) + } + return nil +} + +// IpamInfo contains all the ipam related operational info for a network +type IpamInfo struct { + PoolID string + Meta map[string]string + driverapi.IPAMData +} + +// MarshalJSON encodes IpamInfo into json message +func (i *IpamInfo) MarshalJSON() ([]byte, error) { + m := map[string]interface{}{ + "PoolID": i.PoolID, + } + v, err := json.Marshal(&i.IPAMData) + if err != nil { + return nil, err + } + m["IPAMData"] = string(v) + + if i.Meta != nil { + m["Meta"] = i.Meta + } + return json.Marshal(m) +} + +// UnmarshalJSON decodes json message into PoolData +func (i *IpamInfo) UnmarshalJSON(data []byte) error { + var ( + m map[string]interface{} + err error + ) + if err = json.Unmarshal(data, &m); err != nil { + return err + } + i.PoolID = m["PoolID"].(string) + if v, ok := m["Meta"]; ok { + b, _ := json.Marshal(v) + if err = json.Unmarshal(b, &i.Meta); err != nil { + return err + } + } + if v, ok := m["IPAMData"]; ok { + if err = json.Unmarshal([]byte(v.(string)), &i.IPAMData); err != nil { + return err + } + } + return nil +} + type network struct { - ctrlr *controller - name string - networkType string - id string - driver driverapi.Driver - enableIPv6 bool - endpointCnt uint64 - endpoints endpointTable - generic options.Generic - dbIndex uint64 - svcRecords svcMap - dbExists bool - persist bool - stopWatchCh chan struct{} - dataScope datastore.DataScope + ctrlr *controller + name string + networkType string + id string + ipamType string + addrSpace string + ipamV4Config []*IpamConf + ipamV6Config []*IpamConf + ipamV4Info []*IpamInfo + ipamV6Info []*IpamInfo + enableIPv6 bool + epCnt *endpointCnt + generic options.Generic + dbIndex uint64 + svcRecords svcMap + dbExists bool + persist bool + stopWatchCh chan struct{} + drvOnce *sync.Once sync.Mutex } @@ -92,11 +181,7 @@ func (n *network) Type() string { n.Lock() defer n.Unlock() - if n.driver == nil { - return "" - } - - return n.driver.Type() + return n.networkType } func (n *network) Key() []string { @@ -148,28 +233,116 @@ func (n *network) Skip() bool { return !n.persist } -func (n *network) DataScope() datastore.DataScope { +func (n *network) New() datastore.KVObject { n.Lock() defer n.Unlock() - return n.dataScope + + return &network{ + ctrlr: n.ctrlr, + drvOnce: &sync.Once{}, + } } -func (n *network) EndpointCnt() uint64 { +// CopyTo deep copies to the destination IpamConfig +func (c *IpamConf) CopyTo(dstC *IpamConf) error { + dstC.PreferredPool = c.PreferredPool + dstC.SubPool = c.SubPool + dstC.Gateway = c.Gateway + if c.Options != nil { + dstC.Options = make(map[string]string, len(c.Options)) + for k, v := range c.Options { + dstC.Options[k] = v + } + } + if c.AuxAddresses != nil { + dstC.AuxAddresses = make(map[string]string, len(c.AuxAddresses)) + for k, v := range c.AuxAddresses { + dstC.AuxAddresses[k] = v + } + } + return nil +} + +// CopyTo deep copies to the destination IpamInfo +func (i *IpamInfo) CopyTo(dstI *IpamInfo) error { + dstI.PoolID = i.PoolID + if i.Meta != nil { + dstI.Meta = make(map[string]string) + for k, v := range i.Meta { + dstI.Meta[k] = v + } + } + + dstI.AddressSpace = i.AddressSpace + dstI.Pool = types.GetIPNetCopy(i.Pool) + dstI.Gateway = types.GetIPNetCopy(i.Gateway) + + if i.AuxAddresses != nil { + dstI.AuxAddresses = make(map[string]*net.IPNet) + for k, v := range i.AuxAddresses { + dstI.AuxAddresses[k] = types.GetIPNetCopy(v) + } + } + + return nil +} + +func (n *network) CopyTo(o datastore.KVObject) error { n.Lock() defer n.Unlock() - return n.endpointCnt + + dstN := o.(*network) + dstN.name = n.name + dstN.id = n.id + dstN.networkType = n.networkType + dstN.ipamType = n.ipamType + dstN.enableIPv6 = n.enableIPv6 + dstN.persist = n.persist + dstN.dbIndex = n.dbIndex + dstN.dbExists = n.dbExists + dstN.drvOnce = n.drvOnce + + for _, v4conf := range n.ipamV4Config { + dstV4Conf := &IpamConf{} + v4conf.CopyTo(dstV4Conf) + dstN.ipamV4Config = append(dstN.ipamV4Config, dstV4Conf) + } + + for _, v4info := range n.ipamV4Info { + dstV4Info := &IpamInfo{} + v4info.CopyTo(dstV4Info) + dstN.ipamV4Info = append(dstN.ipamV4Info, dstV4Info) + } + + for _, v6conf := range n.ipamV6Config { + dstV6Conf := &IpamConf{} + v6conf.CopyTo(dstV6Conf) + dstN.ipamV6Config = append(dstN.ipamV6Config, dstV6Conf) + } + + for _, v6info := range n.ipamV6Info { + dstV6Info := &IpamInfo{} + v6info.CopyTo(dstV6Info) + dstN.ipamV6Info = append(dstN.ipamV6Info, dstV6Info) + } + + dstN.generic = options.Generic{} + for k, v := range n.generic { + dstN.generic[k] = v + } + + return nil } -func (n *network) IncEndpointCnt() { - n.Lock() - n.endpointCnt++ - n.Unlock() +func (n *network) DataScope() string { + return n.driverScope() } -func (n *network) DecEndpointCnt() { +func (n *network) getEpCnt() *endpointCnt { n.Lock() - n.endpointCnt-- - n.Unlock() + defer n.Unlock() + + return n.epCnt } // TODO : Can be made much more generic with the help of reflection (but has some golang limitations) @@ -178,10 +351,41 @@ func (n *network) MarshalJSON() ([]byte, error) { netMap["name"] = n.name netMap["id"] = n.id netMap["networkType"] = n.networkType - netMap["endpointCnt"] = n.endpointCnt + netMap["ipamType"] = n.ipamType + netMap["addrSpace"] = n.addrSpace netMap["enableIPv6"] = n.enableIPv6 - netMap["generic"] = n.generic + if n.generic != nil { + netMap["generic"] = n.generic + } netMap["persist"] = n.persist + if len(n.ipamV4Config) > 0 { + ics, err := json.Marshal(n.ipamV4Config) + if err != nil { + return nil, err + } + netMap["ipamV4Config"] = string(ics) + } + if len(n.ipamV4Info) > 0 { + iis, err := json.Marshal(n.ipamV4Info) + if err != nil { + return nil, err + } + netMap["ipamV4Info"] = string(iis) + } + if len(n.ipamV6Config) > 0 { + ics, err := json.Marshal(n.ipamV6Config) + if err != nil { + return nil, err + } + netMap["ipamV6Config"] = string(ics) + } + if len(n.ipamV6Info) > 0 { + iis, err := json.Marshal(n.ipamV6Info) + if err != nil { + return nil, err + } + netMap["ipamV6Info"] = string(iis) + } return json.Marshal(netMap) } @@ -194,13 +398,53 @@ func (n *network) UnmarshalJSON(b []byte) (err error) { n.name = netMap["name"].(string) n.id = netMap["id"].(string) n.networkType = netMap["networkType"].(string) - n.endpointCnt = uint64(netMap["endpointCnt"].(float64)) n.enableIPv6 = netMap["enableIPv6"].(bool) - if netMap["generic"] != nil { - n.generic = netMap["generic"].(map[string]interface{}) + + if v, ok := netMap["generic"]; ok { + n.generic = v.(map[string]interface{}) + // Restore labels in their map[string]string form + if v, ok := n.generic[netlabel.GenericData]; ok { + var lmap map[string]string + ba, err := json.Marshal(v) + if err != nil { + return err + } + if err := json.Unmarshal(ba, &lmap); err != nil { + return err + } + n.generic[netlabel.GenericData] = lmap + } } - if netMap["persist"] != nil { - n.persist = netMap["persist"].(bool) + if v, ok := netMap["persist"]; ok { + n.persist = v.(bool) + } + if v, ok := netMap["ipamType"]; ok { + n.ipamType = v.(string) + } else { + n.ipamType = ipamapi.DefaultIPAM + } + if v, ok := netMap["addrSpace"]; ok { + n.addrSpace = v.(string) + } + if v, ok := netMap["ipamV4Config"]; ok { + if err := json.Unmarshal([]byte(v.(string)), &n.ipamV4Config); err != nil { + return err + } + } + if v, ok := netMap["ipamV4Info"]; ok { + if err := json.Unmarshal([]byte(v.(string)), &n.ipamV4Info); err != nil { + return err + } + } + if v, ok := netMap["ipamV6Config"]; ok { + if err := json.Unmarshal([]byte(v.(string)), &n.ipamV6Config); err != nil { + return err + } + } + if v, ok := netMap["ipamV6Info"]; ok { + if err := json.Unmarshal([]byte(v.(string)), &n.ipamV6Info); err != nil { + return err + } } return nil } @@ -228,6 +472,39 @@ func NetworkOptionPersist(persist bool) NetworkOption { } } +// NetworkOptionIpam function returns an option setter for the ipam configuration for this network +func NetworkOptionIpam(ipamDriver string, addrSpace string, ipV4 []*IpamConf, ipV6 []*IpamConf) NetworkOption { + return func(n *network) { + if ipamDriver != "" { + n.ipamType = ipamDriver + } + n.addrSpace = addrSpace + n.ipamV4Config = ipV4 + n.ipamV6Config = ipV6 + } +} + +// NetworkOptionLabels function returns an option setter for any parameter described by a map +func NetworkOptionLabels(labels map[string]string) NetworkOption { + return func(n *network) { + if n.generic == nil { + n.generic = make(map[string]interface{}) + } + if labels == nil { + labels = make(map[string]string) + } + // Store the options + n.generic[netlabel.GenericData] = labels + // Decode and store the endpoint options of libnetwork interest + if val, ok := labels[netlabel.EnableIPv6]; ok { + var err error + if n.enableIPv6, err = strconv.ParseBool(val); err != nil { + log.Warnf("Failed to parse %s' value: %s (%s)", netlabel.EnableIPv6, val, err.Error()) + } + } + } +} + func (n *network) processOptions(options ...NetworkOption) { for _, opt := range options { if opt != nil { @@ -236,95 +513,121 @@ func (n *network) processOptions(options ...NetworkOption) { } } -func (n *network) Delete() error { - var err error +func (n *network) driverScope() string { + c := n.getController() - n.Lock() - ctrlr := n.ctrlr - n.Unlock() - - ctrlr.Lock() - _, ok := ctrlr.networks[n.id] - ctrlr.Unlock() + c.Lock() + // Check if a driver for the specified network type is available + dd, ok := c.drivers[n.networkType] + c.Unlock() if !ok { - return &UnknownNetworkError{name: n.name, id: n.id} + var err error + dd, err = c.loadDriver(n.networkType) + if err != nil { + // If driver could not be resolved simply return an empty string + return "" + } } - numEps := n.EndpointCnt() + return dd.capability.DataScope +} + +func (n *network) driver() (driverapi.Driver, error) { + c := n.getController() + + c.Lock() + // Check if a driver for the specified network type is available + dd, ok := c.drivers[n.networkType] + c.Unlock() + + if !ok { + var err error + dd, err = c.loadDriver(n.networkType) + if err != nil { + return nil, err + } + } + + return dd.driver, nil +} + +func (n *network) Delete() error { + n.Lock() + c := n.ctrlr + name := n.name + id := n.id + n.Unlock() + + n, err := c.getNetworkFromStore(id) + if err != nil { + return &UnknownNetworkError{name: name, id: id} + } + + numEps := n.getEpCnt().EndpointCnt() if numEps != 0 { return &ActiveEndpointsError{name: n.name, id: n.id} } - // deleteNetworkFromStore performs an atomic delete operation and the network.endpointCnt field will help - // prevent any possible race between endpoint join and network delete - if err = ctrlr.deleteFromStore(n); err != nil { - if err == datastore.ErrKeyModified { - return types.InternalErrorf("operation in progress. delete failed for network %s. Please try again.") - } + if err = n.deleteNetwork(); err != nil { return err } - defer func() { if err != nil { - n.dbExists = false - if e := ctrlr.updateToStore(n); e != nil { - log.Warnf("failed to recreate network in store %s : %v", n.name, e) + if e := c.addNetwork(n); e != nil { + log.Warnf("failed to rollback deleteNetwork for network %s: %v", + n.Name(), err) } } }() - if err = n.deleteNetwork(); err != nil { - return err + // deleteFromStore performs an atomic delete operation and the + // network.epCnt will help prevent any possible + // race between endpoint join and network delete + if err = n.getController().deleteFromStore(n.getEpCnt()); err != nil { + return fmt.Errorf("error deleting network endpoint count from store: %v", err) } + if err = n.getController().deleteFromStore(n); err != nil { + return fmt.Errorf("error deleting network from store: %v", err) + } + + n.ipamRelease() return nil } func (n *network) deleteNetwork() error { - n.Lock() - id := n.id - d := n.driver - n.ctrlr.Lock() - delete(n.ctrlr.networks, id) - n.ctrlr.Unlock() - n.Unlock() + d, err := n.driver() + if err != nil { + return fmt.Errorf("failed deleting network: %v", err) + } - if err := d.DeleteNetwork(n.id); err != nil { + if err := d.DeleteNetwork(n.ID()); err != nil { // Forbidden Errors should be honored if _, ok := err.(types.ForbiddenError); ok { - n.ctrlr.Lock() - n.ctrlr.networks[n.id] = n - n.ctrlr.Unlock() return err } - log.Warnf("driver error deleting network %s : %v", n.name, err) + + if _, ok := err.(types.MaskableError); !ok { + log.Warnf("driver error deleting network %s : %v", n.name, err) + } } - n.stopWatch() + return nil } func (n *network) addEndpoint(ep *endpoint) error { - var err error - n.Lock() - n.endpoints[ep.id] = ep - d := n.driver - n.Unlock() - - defer func() { - if err != nil { - n.Lock() - delete(n.endpoints, ep.id) - n.Unlock() - } - }() - - err = d.CreateEndpoint(n.id, ep.id, ep, ep.generic) + d, err := n.driver() if err != nil { - return types.InternalErrorf("failed to create endpoint %s on network %s: %v", ep.Name(), n.Name(), err) + return fmt.Errorf("failed to add endpoint: %v", err) + } + + err = d.CreateEndpoint(n.id, ep.id, ep.Interface(), ep.generic) + if err != nil { + return types.InternalErrorf("failed to create endpoint %s on network %s: %v", + ep.Name(), n.Name(), err) } - n.updateSvcRecord(ep, true) return nil } @@ -338,54 +641,69 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi return nil, types.ForbiddenErrorf("service endpoint with name %s already exists", name) } - ep := &endpoint{name: name, - generic: make(map[string]interface{})} + ep := &endpoint{name: name, generic: make(map[string]interface{}), iface: &endpointInterface{}} ep.id = stringid.GenerateRandomID() + + // Initialize ep.network with a possibly stale copy of n. We need this to get network from + // store. But once we get it from store we will have the most uptodate copy possible. ep.network = n + ep.network, err = ep.getNetworkFromStore() + if err != nil { + return nil, fmt.Errorf("failed to get network during CreateEndpoint: %v", err) + } + n = ep.network + ep.processOptions(options...) - n.Lock() - ctrlr := n.ctrlr - n.Unlock() - - n.IncEndpointCnt() - if err = ctrlr.updateToStore(n); err != nil { + if err = ep.assignAddress(); err != nil { return nil, err } defer func() { if err != nil { - n.DecEndpointCnt() - if err = ctrlr.updateToStore(n); err != nil { - log.Warnf("endpoint count cleanup failed when updating network for %s : %v", name, err) - } + ep.releaseAddress() } }() + if err = n.addEndpoint(ep); err != nil { return nil, err } defer func() { if err != nil { - if e := ep.Delete(); ep != nil { + if e := ep.deleteEndpoint(); e != nil { log.Warnf("cleaning up endpoint failed %s : %v", name, e) } } }() - if !ep.isLocalScoped() { - if err = ctrlr.updateToStore(ep); err != nil { - return nil, err + if err = n.getController().updateToStore(ep); err != nil { + return nil, err + } + defer func() { + if err != nil { + if e := n.getController().deleteFromStore(ep); e != nil { + log.Warnf("error rolling back endpoint %s from store: %v", name, e) + } } + }() + + // Increment endpoint count to indicate completion of endpoint addition + if err = n.getEpCnt().IncEndpointCnt(); err != nil { + return nil, err } return ep, nil } func (n *network) Endpoints() []Endpoint { - n.Lock() - defer n.Unlock() - list := make([]Endpoint, 0, len(n.endpoints)) - for _, e := range n.endpoints { - list = append(list, e) + var list []Endpoint + + endpoints, err := n.getEndpointsFromStore() + if err != nil { + log.Error(err) + } + + for _, ep := range endpoints { + list = append(list, ep) } return list @@ -426,28 +744,32 @@ func (n *network) EndpointByID(id string) (Endpoint, error) { if id == "" { return nil, ErrInvalidID(id) } - n.Lock() - defer n.Unlock() - if e, ok := n.endpoints[id]; ok { - return e, nil + + ep, err := n.getEndpointFromStore(id) + if err != nil { + return nil, ErrNoSuchEndpoint(id) } - return nil, ErrNoSuchEndpoint(id) + + return ep, nil } -func (n *network) isGlobalScoped() bool { - return n.DataScope() == datastore.GlobalScope -} +func (n *network) updateSvcRecord(ep *endpoint, localEps []*endpoint, isAdd bool) { + c := n.getController() + sr, ok := c.svcDb[n.ID()] + if !ok { + c.svcDb[n.ID()] = svcMap{} + sr = c.svcDb[n.ID()] + } -func (n *network) updateSvcRecord(ep *endpoint, isAdd bool) { n.Lock() var recs []etchosts.Record - if iface := ep.Iface(); iface != nil { + if iface := ep.Iface(); iface.Address() != nil { if isAdd { - n.svcRecords[ep.Name()] = iface.Address().IP - n.svcRecords[ep.Name()+"."+n.name] = iface.Address().IP + sr[ep.Name()] = iface.Address().IP + sr[ep.Name()+"."+n.name] = iface.Address().IP } else { - delete(n.svcRecords, ep.Name()) - delete(n.svcRecords, ep.Name()+"."+n.name) + delete(sr, ep.Name()) + delete(sr, ep.Name()+"."+n.name) } recs = append(recs, etchosts.Record{ @@ -468,12 +790,11 @@ func (n *network) updateSvcRecord(ep *endpoint, isAdd bool) { } var sbList []*sandbox - n.WalkEndpoints(func(e Endpoint) bool { - if sb, hasSandbox := e.(*endpoint).getSandbox(); hasSandbox { + for _, ep := range localEps { + if sb, hasSandbox := ep.getSandbox(); hasSandbox { sbList = append(sbList, sb) } - return false - }) + } for _, sb := range sbList { if isAdd { @@ -489,7 +810,9 @@ func (n *network) getSvcRecords() []etchosts.Record { defer n.Unlock() var recs []etchosts.Record - for h, ip := range n.svcRecords { + sr, _ := n.ctrlr.svcDb[n.id] + + for h, ip := range sr { recs = append(recs, etchosts.Record{ Hosts: h, IP: ip.String(), @@ -504,3 +827,268 @@ func (n *network) getController() *controller { defer n.Unlock() return n.ctrlr } + +func (n *network) ipamAllocate() error { + // For now also exclude bridge from using new ipam + if n.Type() == "host" || n.Type() == "null" { + return nil + } + + ipam, err := n.getController().getIpamDriver(n.ipamType) + if err != nil { + return err + } + + if n.addrSpace == "" { + if n.addrSpace, err = n.deriveAddressSpace(); err != nil { + return err + } + } + + err = n.ipamAllocateVersion(4, ipam) + if err != nil { + return err + } + + defer func() { + if err != nil { + n.ipamReleaseVersion(4, ipam) + } + }() + + return n.ipamAllocateVersion(6, ipam) +} + +func (n *network) ipamAllocateVersion(ipVer int, ipam ipamapi.Ipam) error { + var ( + cfgList *[]*IpamConf + infoList *[]*IpamInfo + err error + ) + + switch ipVer { + case 4: + cfgList = &n.ipamV4Config + infoList = &n.ipamV4Info + case 6: + cfgList = &n.ipamV6Config + infoList = &n.ipamV6Info + default: + return types.InternalErrorf("incorrect ip version passed to ipam allocate: %d", ipVer) + } + + if len(*cfgList) == 0 { + if ipVer == 6 { + return nil + } + *cfgList = []*IpamConf{&IpamConf{}} + } + + *infoList = make([]*IpamInfo, len(*cfgList)) + + log.Debugf("Allocating IPv%d pools for network %s (%s)", ipVer, n.Name(), n.ID()) + + for i, cfg := range *cfgList { + if err = cfg.Validate(); err != nil { + return err + } + d := &IpamInfo{} + (*infoList)[i] = d + + d.PoolID, d.Pool, d.Meta, err = ipam.RequestPool(n.addrSpace, cfg.PreferredPool, cfg.SubPool, cfg.Options, ipVer == 6) + if err != nil { + return err + } + + defer func() { + if err != nil { + if err := ipam.ReleasePool(d.PoolID); err != nil { + log.Warnf("Failed to release address pool %s after failure to create network %s (%s)", d.PoolID, n.Name(), n.ID()) + } + } + }() + + if gws, ok := d.Meta[netlabel.Gateway]; ok { + if d.Gateway, err = types.ParseCIDR(gws); err != nil { + return types.BadRequestErrorf("failed to parse gateway address (%v) returned by ipam driver: %v", gws, err) + } + } + + // If user requested a specific gateway, libnetwork will allocate it + // irrespective of whether ipam driver returned a gateway already. + // If none of the above is true, libnetwork will allocate one. + if cfg.Gateway != "" || d.Gateway == nil { + if d.Gateway, _, err = ipam.RequestAddress(d.PoolID, net.ParseIP(cfg.Gateway), nil); err != nil { + return types.InternalErrorf("failed to allocate gateway (%v): %v", cfg.Gateway, err) + } + } + + // Auxiliary addresses must be part of the master address pool + // If they fall into the container addressable pool, libnetwork will reserve them + if cfg.AuxAddresses != nil { + var ip net.IP + d.IPAMData.AuxAddresses = make(map[string]*net.IPNet, len(cfg.AuxAddresses)) + for k, v := range cfg.AuxAddresses { + if ip = net.ParseIP(v); ip == nil { + return types.BadRequestErrorf("non parsable secondary ip address (%s:%s) passed for network %s", k, v, n.Name()) + } + if !d.Pool.Contains(ip) { + return types.ForbiddenErrorf("auxilairy address: (%s:%s) must belong to the master pool: %s", k, v, d.Pool) + } + // Attempt reservation in the container addressable pool, silent the error if address does not belong to that pool + if d.IPAMData.AuxAddresses[k], _, err = ipam.RequestAddress(d.PoolID, ip, nil); err != nil && err != ipamapi.ErrIPOutOfRange { + return types.InternalErrorf("failed to allocate secondary ip address (%s:%s): %v", k, v, err) + } + } + } + } + + return nil +} + +func (n *network) ipamRelease() { + // For now exclude host and null + if n.Type() == "host" || n.Type() == "null" { + return + } + ipam, err := n.getController().getIpamDriver(n.ipamType) + if err != nil { + log.Warnf("Failed to retrieve ipam driver to release address pool(s) on delete of network %s (%s): %v", n.Name(), n.ID(), err) + return + } + n.ipamReleaseVersion(4, ipam) + n.ipamReleaseVersion(6, ipam) +} + +func (n *network) ipamReleaseVersion(ipVer int, ipam ipamapi.Ipam) { + var infoList []*IpamInfo + + switch ipVer { + case 4: + infoList = n.ipamV4Info + case 6: + infoList = n.ipamV6Info + default: + log.Warnf("incorrect ip version passed to ipam release: %d", ipVer) + return + } + + if infoList == nil { + return + } + + log.Debugf("releasing IPv%d pools from network %s (%s)", ipVer, n.Name(), n.ID()) + + for _, d := range infoList { + if d.Gateway != nil { + if err := ipam.ReleaseAddress(d.PoolID, d.Gateway.IP); err != nil { + log.Warnf("Failed to release gateway ip address %s on delete of network %s (%s): %v", d.Gateway.IP, n.Name(), n.ID(), err) + } + } + if d.IPAMData.AuxAddresses != nil { + for k, nw := range d.IPAMData.AuxAddresses { + if d.Pool.Contains(nw.IP) { + if err := ipam.ReleaseAddress(d.PoolID, nw.IP); err != nil && err != ipamapi.ErrIPOutOfRange { + log.Warnf("Failed to release secondary ip address %s (%v) on delete of network %s (%s): %v", k, nw.IP, n.Name(), n.ID(), err) + } + } + } + } + if err := ipam.ReleasePool(d.PoolID); err != nil { + log.Warnf("Failed to release address pool %s on delete of network %s (%s): %v", d.PoolID, n.Name(), n.ID(), err) + } + } +} + +func (n *network) getIPInfo(ipVer int) []*IpamInfo { + var info []*IpamInfo + switch ipVer { + case 4: + info = n.ipamV4Info + case 6: + info = n.ipamV6Info + default: + return nil + } + l := make([]*IpamInfo, 0, len(info)) + n.Lock() + for _, d := range info { + l = append(l, d) + } + n.Unlock() + return l +} + +func (n *network) getIPData(ipVer int) []driverapi.IPAMData { + var info []*IpamInfo + switch ipVer { + case 4: + info = n.ipamV4Info + case 6: + info = n.ipamV6Info + default: + return nil + } + l := make([]driverapi.IPAMData, 0, len(info)) + n.Lock() + for _, d := range info { + l = append(l, d.IPAMData) + } + n.Unlock() + return l +} + +func (n *network) deriveAddressSpace() (string, error) { + c := n.getController() + c.Lock() + ipd, ok := c.ipamDrivers[n.ipamType] + c.Unlock() + if !ok { + return "", types.NotFoundErrorf("could not find ipam driver %s to get default address space", n.ipamType) + } + if n.DataScope() == datastore.GlobalScope { + return ipd.defaultGlobalAddressSpace, nil + } + return ipd.defaultLocalAddressSpace, nil +} + +func (n *network) Info() NetworkInfo { + return n +} + +func (n *network) Labels() map[string]string { + n.Lock() + defer n.Unlock() + if n.generic != nil { + if m, ok := n.generic[netlabel.GenericData]; ok { + return m.(map[string]string) + } + } + return map[string]string{} +} + +func (n *network) Scope() string { + return n.driverScope() +} + +func (n *network) IpamConfig() (string, []*IpamConf, []*IpamConf) { + n.Lock() + defer n.Unlock() + + v4L := make([]*IpamConf, len(n.ipamV4Config)) + v6L := make([]*IpamConf, len(n.ipamV6Config)) + + for i, c := range n.ipamV4Config { + cc := &IpamConf{} + c.CopyTo(cc) + v4L[i] = cc + } + + for i, c := range n.ipamV6Config { + cc := &IpamConf{} + c.CopyTo(cc) + v6L[i] = cc + } + + return n.ipamType, v4L, v6L +} diff --git a/vendor/src/github.com/docker/libnetwork/osl/interface_linux.go b/vendor/src/github.com/docker/libnetwork/osl/interface_linux.go index 0b0b0d9fa7..d57e7601b1 100644 --- a/vendor/src/github.com/docker/libnetwork/osl/interface_linux.go +++ b/vendor/src/github.com/docker/libnetwork/osl/interface_linux.go @@ -156,7 +156,7 @@ func (i *nwIface) Remove() error { } // Returns the sandbox's side veth interface statistics -func (i *nwIface) Statistics() (*InterfaceStatistics, error) { +func (i *nwIface) Statistics() (*types.InterfaceStatistics, error) { i.Lock() n := i.ns i.Unlock() @@ -165,7 +165,7 @@ func (i *nwIface) Statistics() (*InterfaceStatistics, error) { path := n.path n.Unlock() - s := &InterfaceStatistics{} + s := &types.InterfaceStatistics{} err := nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error { // For some reason ioutil.ReadFile(netStatsFile) reads the file in @@ -356,7 +356,7 @@ const ( base = "[ ]*%s:([ ]+[0-9]+){16}" ) -func scanInterfaceStats(data, ifName string, i *InterfaceStatistics) error { +func scanInterfaceStats(data, ifName string, i *types.InterfaceStatistics) error { var ( bktStr string bkt uint64 diff --git a/vendor/src/github.com/docker/libnetwork/osl/sandbox.go b/vendor/src/github.com/docker/libnetwork/osl/sandbox.go index 9e87f472b8..3a824ae6ad 100644 --- a/vendor/src/github.com/docker/libnetwork/osl/sandbox.go +++ b/vendor/src/github.com/docker/libnetwork/osl/sandbox.go @@ -2,7 +2,6 @@ package osl import ( - "fmt" "net" "github.com/docker/libnetwork/types" @@ -150,22 +149,5 @@ type Interface interface { Remove() error // Statistics returns the statistics for this interface - Statistics() (*InterfaceStatistics, error) -} - -// InterfaceStatistics represents the interface's statistics -type InterfaceStatistics struct { - RxBytes uint64 - RxPackets uint64 - RxErrors uint64 - RxDropped uint64 - TxBytes uint64 - TxPackets uint64 - TxErrors uint64 - TxDropped uint64 -} - -func (is *InterfaceStatistics) String() string { - return fmt.Sprintf("\nRxBytes: %d, RxPackets: %d, RxErrors: %d, RxDropped: %d, TxBytes: %d, TxPackets: %d, TxErrors: %d, TxDropped: %d", - is.RxBytes, is.RxPackets, is.RxErrors, is.RxDropped, is.TxBytes, is.TxPackets, is.TxErrors, is.TxDropped) + Statistics() (*types.InterfaceStatistics, error) } diff --git a/vendor/src/github.com/docker/libnetwork/resolvconf/dns/resolvconf.go b/vendor/src/github.com/docker/libnetwork/resolvconf/dns/resolvconf.go index d581a1913d..f981b1fc85 100644 --- a/vendor/src/github.com/docker/libnetwork/resolvconf/dns/resolvconf.go +++ b/vendor/src/github.com/docker/libnetwork/resolvconf/dns/resolvconf.go @@ -5,7 +5,7 @@ import ( ) // IPLocalhost is a regex patter for localhost IP address range. -const IPLocalhost = `((127\.([0-9]{1,3}.){2}[0-9]{1,3})|(::1))` +const IPLocalhost = `((127\.([0-9]{1,3}\.){2}[0-9]{1,3})|(::1))` var localhostIPRegexp = regexp.MustCompile(IPLocalhost) diff --git a/vendor/src/github.com/docker/libnetwork/sandbox.go b/vendor/src/github.com/docker/libnetwork/sandbox.go index 40fc7f6de6..454ed57715 100644 --- a/vendor/src/github.com/docker/libnetwork/sandbox.go +++ b/vendor/src/github.com/docker/libnetwork/sandbox.go @@ -28,7 +28,7 @@ type Sandbox interface { // Labels returns the sandbox's labels Labels() map[string]interface{} // Statistics retrieves the interfaces' statistics for the sandbox - Statistics() (map[string]*osl.InterfaceStatistics, error) + Statistics() (map[string]*types.InterfaceStatistics, error) // Refresh leaves all the endpoints, resets and re-apply the options, // re-joins all the endpoints without destroying the osl sandbox Refresh(options ...SandboxOption) error @@ -64,6 +64,8 @@ type sandbox struct { endpoints epHeap epPriority map[string]int joinLeaveDone chan struct{} + dbIndex uint64 + dbExists bool sync.Mutex } @@ -126,8 +128,8 @@ func (sb *sandbox) Labels() map[string]interface{} { return sb.config.generic } -func (sb *sandbox) Statistics() (map[string]*osl.InterfaceStatistics, error) { - m := make(map[string]*osl.InterfaceStatistics) +func (sb *sandbox) Statistics() (map[string]*types.InterfaceStatistics, error) { + m := make(map[string]*types.InterfaceStatistics) if sb.osSbox == nil { return m, nil @@ -153,15 +155,24 @@ func (sb *sandbox) Delete() error { if ep.endpointInGWNetwork() { continue } + if err := ep.Leave(sb); err != nil { log.Warnf("Failed detaching sandbox %s from endpoint %s: %v\n", sb.ID(), ep.ID(), err) } + + if err := ep.Delete(); err != nil { + log.Warnf("Failed deleting endpoint %s: %v\n", ep.ID(), err) + } } if sb.osSbox != nil { sb.osSbox.Destroy() } + if err := sb.storeDelete(); err != nil { + log.Warnf("Failed to delete sandbox %s from store: %v", sb.ID(), err) + } + c.Lock() delete(c.sandboxes, sb.ID()) c.Unlock() @@ -247,6 +258,19 @@ func (sb *sandbox) getConnectedEndpoints() []*endpoint { return eps } +func (sb *sandbox) getEndpoint(id string) *endpoint { + sb.Lock() + defer sb.Unlock() + + for _, ep := range sb.endpoints { + if ep.id == id { + return ep + } + } + + return nil +} + func (sb *sandbox) updateGateway(ep *endpoint) error { sb.Lock() osSbox := sb.osSbox @@ -283,15 +307,21 @@ func (sb *sandbox) SetKey(basePath string) error { } sb.Lock() - if sb.osSbox != nil { - sb.Unlock() - return types.ForbiddenErrorf("failed to set sandbox key : already assigned") - } + osSbox := sb.osSbox sb.Unlock() - osSbox, err := osl.GetSandboxForExternalKey(basePath, sb.Key()) + + if osSbox != nil { + // If we already have an OS sandbox, release the network resources from that + // and destroy the OS snab. We are moving into a new home further down. Note that none + // of the network resources gets destroyed during the move. + sb.releaseOSSbox() + } + + osSbox, err = osl.GetSandboxForExternalKey(basePath, sb.Key()) if err != nil { return err } + sb.Lock() sb.osSbox = osSbox sb.Unlock() @@ -311,6 +341,45 @@ func (sb *sandbox) SetKey(basePath string) error { return nil } +func releaseOSSboxResources(osSbox osl.Sandbox, ep *endpoint) { + for _, i := range osSbox.Info().Interfaces() { + // Only remove the interfaces owned by this endpoint from the sandbox. + if ep.hasInterface(i.SrcName()) { + if err := i.Remove(); err != nil { + log.Debugf("Remove interface failed: %v", err) + } + } + } + + ep.Lock() + joinInfo := ep.joinInfo + ep.Unlock() + + // Remove non-interface routes. + for _, r := range joinInfo.StaticRoutes { + if err := osSbox.RemoveStaticRoute(r); err != nil { + log.Debugf("Remove route failed: %v", err) + } + } +} + +func (sb *sandbox) releaseOSSbox() { + sb.Lock() + osSbox := sb.osSbox + sb.osSbox = nil + sb.Unlock() + + if osSbox == nil { + return + } + + for _, ep := range sb.getConnectedEndpoints() { + releaseOSSboxResources(osSbox, ep) + } + + osSbox.Destroy() +} + func (sb *sandbox) populateNetworkResources(ep *endpoint) error { sb.Lock() if sb.osSbox == nil { @@ -324,12 +393,12 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error { i := ep.iface ep.Unlock() - if i != nil { + if i.srcName != "" { var ifaceOptions []osl.IfaceOption - ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().Address(&i.addr), sb.osSbox.InterfaceOptions().Routes(i.routes)) - if i.addrv6.IP.To16() != nil { - ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().AddressIPv6(&i.addrv6)) + ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().Address(i.addr), sb.osSbox.InterfaceOptions().Routes(i.routes)) + if i.addrv6 != nil && i.addrv6.IP.To16() != nil { + ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().AddressIPv6(i.addrv6)) } if err := sb.osSbox.AddInterface(i.srcName, i.dstPrefix, ifaceOptions...); err != nil { @@ -356,33 +425,21 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error { } } } - return nil + return sb.storeUpdate() } -func (sb *sandbox) clearNetworkResources(ep *endpoint) error { +func (sb *sandbox) clearNetworkResources(origEp *endpoint) error { + ep := sb.getEndpoint(origEp.id) + if ep == nil { + return fmt.Errorf("could not find the sandbox endpoint data for endpoint %s", + ep.name) + } + sb.Lock() osSbox := sb.osSbox sb.Unlock() if osSbox != nil { - for _, i := range osSbox.Info().Interfaces() { - // Only remove the interfaces owned by this endpoint from the sandbox. - if ep.hasInterface(i.SrcName()) { - if err := i.Remove(); err != nil { - log.Debugf("Remove interface failed: %v", err) - } - } - } - - ep.Lock() - joinInfo := ep.joinInfo - ep.Unlock() - - // Remove non-interface routes. - for _, r := range joinInfo.StaticRoutes { - if err := osSbox.RemoveStaticRoute(r); err != nil { - log.Debugf("Remove route failed: %v", err) - } - } + releaseOSSboxResources(osSbox, ep) } sb.Lock() @@ -423,7 +480,7 @@ func (sb *sandbox) clearNetworkResources(ep *endpoint) error { sb.updateGateway(gwepAfter) } - return nil + return sb.storeUpdate() } const ( @@ -837,7 +894,7 @@ func (eh epHeap) Less(i, j int) bool { cjp = 0 } if cip == cjp { - return eh[i].getNetwork().Name() < eh[j].getNetwork().Name() + return eh[i].network.Name() < eh[j].network.Name() } return cip > cjp diff --git a/vendor/src/github.com/docker/libnetwork/sandbox_store.go b/vendor/src/github.com/docker/libnetwork/sandbox_store.go new file mode 100644 index 0000000000..2a86cbc4cc --- /dev/null +++ b/vendor/src/github.com/docker/libnetwork/sandbox_store.go @@ -0,0 +1,211 @@ +package libnetwork + +import ( + "container/heap" + "encoding/json" + + "github.com/Sirupsen/logrus" + "github.com/docker/libnetwork/datastore" + "github.com/docker/libnetwork/osl" +) + +const ( + sandboxPrefix = "sandbox" +) + +type epState struct { + Eid string + Nid string +} + +type sbState struct { + ID string + Cid string + c *controller + dbIndex uint64 + dbExists bool + Eps []epState +} + +func (sbs *sbState) Key() []string { + return []string{sandboxPrefix, sbs.ID} +} + +func (sbs *sbState) KeyPrefix() []string { + return []string{sandboxPrefix} +} + +func (sbs *sbState) Value() []byte { + b, err := json.Marshal(sbs) + if err != nil { + return nil + } + return b +} + +func (sbs *sbState) SetValue(value []byte) error { + return json.Unmarshal(value, sbs) +} + +func (sbs *sbState) Index() uint64 { + sbi, err := sbs.c.SandboxByID(sbs.ID) + if err != nil { + return sbs.dbIndex + } + + sb := sbi.(*sandbox) + maxIndex := sb.dbIndex + if sbs.dbIndex > maxIndex { + maxIndex = sbs.dbIndex + } + + return maxIndex +} + +func (sbs *sbState) SetIndex(index uint64) { + sbs.dbIndex = index + sbs.dbExists = true + + sbi, err := sbs.c.SandboxByID(sbs.ID) + if err != nil { + return + } + + sb := sbi.(*sandbox) + sb.dbIndex = index + sb.dbExists = true +} + +func (sbs *sbState) Exists() bool { + if sbs.dbExists { + return sbs.dbExists + } + + sbi, err := sbs.c.SandboxByID(sbs.ID) + if err != nil { + return false + } + + sb := sbi.(*sandbox) + return sb.dbExists +} + +func (sbs *sbState) Skip() bool { + return false +} + +func (sbs *sbState) New() datastore.KVObject { + return &sbState{c: sbs.c} +} + +func (sbs *sbState) CopyTo(o datastore.KVObject) error { + dstSbs := o.(*sbState) + dstSbs.c = sbs.c + dstSbs.ID = sbs.ID + dstSbs.Cid = sbs.Cid + dstSbs.dbIndex = sbs.dbIndex + dstSbs.dbExists = sbs.dbExists + + for _, eps := range sbs.Eps { + dstSbs.Eps = append(dstSbs.Eps, eps) + } + + return nil +} + +func (sbs *sbState) DataScope() string { + return datastore.LocalScope +} + +func (sb *sandbox) storeUpdate() error { + sbs := &sbState{ + c: sb.controller, + ID: sb.id, + } + + for _, ep := range sb.getConnectedEndpoints() { + eps := epState{ + Nid: ep.getNetwork().ID(), + Eid: ep.ID(), + } + + sbs.Eps = append(sbs.Eps, eps) + } + + return sb.controller.updateToStore(sbs) +} + +func (sb *sandbox) storeDelete() error { + sbs := &sbState{ + c: sb.controller, + ID: sb.id, + Cid: sb.containerID, + dbIndex: sb.dbIndex, + dbExists: sb.dbExists, + } + + return sb.controller.deleteFromStore(sbs) +} + +func (c *controller) sandboxCleanup() { + store := c.getStore(datastore.LocalScope) + if store == nil { + logrus.Errorf("Could not find local scope store while trying to cleanup sandboxes") + return + } + + kvol, err := store.List(datastore.Key(sandboxPrefix), &sbState{c: c}) + if err != nil && err != datastore.ErrKeyNotFound { + logrus.Errorf("failed to get sandboxes for scope %s: %v", store.Scope(), err) + return + } + + // It's normal for no sandboxes to be found. Just bail out. + if err == datastore.ErrKeyNotFound { + return + } + + for _, kvo := range kvol { + sbs := kvo.(*sbState) + + sb := &sandbox{ + id: sbs.ID, + controller: sbs.c, + containerID: sbs.Cid, + endpoints: epHeap{}, + epPriority: map[string]int{}, + dbIndex: sbs.dbIndex, + dbExists: true, + } + + sb.osSbox, err = osl.NewSandbox(sb.Key(), true) + if err != nil { + logrus.Errorf("failed to create new osl sandbox while trying to build sandbox for cleanup: %v", err) + continue + } + + for _, eps := range sbs.Eps { + n, err := c.getNetworkFromStore(eps.Nid) + if err != nil { + logrus.Errorf("getNetworkFromStore for nid %s failed while trying to build sandbox for cleanup: %v", eps.Nid, err) + continue + } + + ep, err := n.getEndpointFromStore(eps.Eid) + if err != nil { + logrus.Errorf("getEndpointFromStore for eid %s failed while trying to build sandbox for cleanup: %v", eps.Eid, err) + continue + } + + heap.Push(&sb.endpoints, ep) + } + + c.Lock() + c.sandboxes[sb.id] = sb + c.Unlock() + + if err := sb.Delete(); err != nil { + logrus.Errorf("failed to delete sandbox %s while trying to cleanup: %v", sb.id, err) + } + } +} diff --git a/vendor/src/github.com/docker/libnetwork/store.go b/vendor/src/github.com/docker/libnetwork/store.go index 1b70cc22be..d5eca874a4 100644 --- a/vendor/src/github.com/docker/libnetwork/store.go +++ b/vendor/src/github.com/docker/libnetwork/store.go @@ -1,392 +1,366 @@ package libnetwork import ( - "encoding/json" "fmt" - "time" log "github.com/Sirupsen/logrus" - "github.com/docker/libkv/store" - "github.com/docker/libnetwork/config" "github.com/docker/libnetwork/datastore" ) -var ( - defaultBoltTimeout = 3 * time.Second - defaultLocalStoreConfig = config.DatastoreCfg{ - Embedded: true, - Client: config.DatastoreClientCfg{ - Provider: "boltdb", - Address: defaultPrefix + "/boltdb.db", - Config: &store.Config{ - Bucket: "libnetwork", - ConnectionTimeout: defaultBoltTimeout, - }, - }, - } -) - -func (c *controller) validateGlobalStoreConfig() bool { - return c.cfg != nil && c.cfg.GlobalStore.Client.Provider != "" && c.cfg.GlobalStore.Client.Address != "" -} - -func (c *controller) initGlobalStore() error { +func (c *controller) initStores() error { c.Lock() - cfg := c.cfg - c.Unlock() - if !c.validateGlobalStoreConfig() { - return fmt.Errorf("globalstore initialization requires a valid configuration") - } - - store, err := datastore.NewDataStore(&cfg.GlobalStore) - if err != nil { - return err - } - c.Lock() - c.globalStore = store - c.Unlock() - - nws, err := c.getNetworksFromStore(true) - if err == nil { - c.processNetworkUpdate(nws, nil) - } else if err != datastore.ErrKeyNotFound { - log.Warnf("failed to read networks from globalstore during init : %v", err) - } - return c.watchNetworks() -} - -func (c *controller) initLocalStore() error { - c.Lock() - cfg := c.cfg - c.Unlock() - localStore, err := datastore.NewDataStore(c.getLocalStoreConfig(cfg)) - if err != nil { - return err - } - c.Lock() - c.localStore = localStore - c.Unlock() - - nws, err := c.getNetworksFromStore(false) - if err == nil { - c.processNetworkUpdate(nws, nil) - } else if err != datastore.ErrKeyNotFound { - log.Warnf("failed to read networks from localstore during init : %v", err) - } - return nil -} - -func (c *controller) getNetworksFromStore(global bool) ([]*store.KVPair, error) { - var cs datastore.DataStore - c.Lock() - if global { - cs = c.globalStore - } else { - cs = c.localStore - } - c.Unlock() - return cs.KVStore().List(datastore.Key(datastore.NetworkKeyPrefix)) -} - -func (c *controller) newNetworkFromStore(n *network) error { - n.Lock() - n.ctrlr = c - n.endpoints = endpointTable{} - n.Unlock() - - return c.addNetwork(n) -} - -func (c *controller) newEndpointFromStore(key string, ep *endpoint) error { - ep.Lock() - n := ep.network - id := ep.id - ep.Unlock() - - _, err := n.EndpointByID(id) - if err != nil { - if _, ok := err.(ErrNoSuchEndpoint); ok { - return n.addEndpoint(ep) - } - } - return err -} - -func (c *controller) updateToStore(kvObject datastore.KV) error { - if kvObject.Skip() { - return nil - } - cs := c.getDataStore(kvObject.DataScope()) - if cs == nil { - log.Debugf("datastore not initialized. kv object %s is not added to the store", datastore.Key(kvObject.Key()...)) - return nil - } - - return cs.PutObjectAtomic(kvObject) -} - -func (c *controller) deleteFromStore(kvObject datastore.KV) error { - if kvObject.Skip() { - return nil - } - cs := c.getDataStore(kvObject.DataScope()) - if cs == nil { - log.Debugf("datastore not initialized. kv object %s is not deleted from datastore", datastore.Key(kvObject.Key()...)) - return nil - } - - if err := cs.DeleteObjectAtomic(kvObject); err != nil { - return err - } - - return nil -} - -func (c *controller) watchNetworks() error { - if !c.validateGlobalStoreConfig() { - return nil - } - - c.Lock() - cs := c.globalStore - c.Unlock() - - networkKey := datastore.Key(datastore.NetworkKeyPrefix) - if err := ensureKeys(networkKey, cs); err != nil { - return fmt.Errorf("failed to ensure if the network keys are valid and present in store: %v", err) - } - nwPairs, err := cs.KVStore().WatchTree(networkKey, nil) - if err != nil { - return err - } - go func() { - for { - select { - case nws := <-nwPairs: - c.Lock() - tmpview := networkTable{} - lview := c.networks - c.Unlock() - for k, v := range lview { - if v.isGlobalScoped() { - tmpview[k] = v - } - } - c.processNetworkUpdate(nws, &tmpview) - - // Delete processing - for k := range tmpview { - c.Lock() - existing, ok := c.networks[k] - c.Unlock() - if !ok { - continue - } - tmp := network{} - if err := c.globalStore.GetObject(datastore.Key(existing.Key()...), &tmp); err != datastore.ErrKeyNotFound { - continue - } - if err := existing.deleteNetwork(); err != nil { - log.Debugf("Delete failed %s: %s", existing.name, err) - } - } - } - } - }() - return nil -} - -func (n *network) watchEndpoints() error { - if n.Skip() || !n.ctrlr.validateGlobalStoreConfig() { - return nil - } - - n.Lock() - cs := n.ctrlr.globalStore - tmp := endpoint{network: n} - n.stopWatchCh = make(chan struct{}) - stopCh := n.stopWatchCh - n.Unlock() - - endpointKey := datastore.Key(tmp.KeyPrefix()...) - if err := ensureKeys(endpointKey, cs); err != nil { - return fmt.Errorf("failed to ensure if the endpoint keys are valid and present in store: %v", err) - } - epPairs, err := cs.KVStore().WatchTree(endpointKey, stopCh) - if err != nil { - return err - } - go func() { - for { - select { - case <-stopCh: - return - case eps := <-epPairs: - n.Lock() - tmpview := endpointTable{} - lview := n.endpoints - n.Unlock() - for k, v := range lview { - if v.network.isGlobalScoped() { - tmpview[k] = v - } - } - n.ctrlr.processEndpointsUpdate(eps, &tmpview) - // Delete processing - for k := range tmpview { - n.Lock() - existing, ok := n.endpoints[k] - n.Unlock() - if !ok { - continue - } - tmp := endpoint{} - if err := cs.GetObject(datastore.Key(existing.Key()...), &tmp); err != datastore.ErrKeyNotFound { - continue - } - if err := existing.deleteEndpoint(); err != nil { - log.Debugf("Delete failed %s: %s", existing.name, err) - } - } - } - } - }() - return nil -} - -func (n *network) stopWatch() { - n.Lock() - if n.stopWatchCh != nil { - close(n.stopWatchCh) - n.stopWatchCh = nil - } - n.Unlock() -} - -func (c *controller) processNetworkUpdate(nws []*store.KVPair, prune *networkTable) { - for _, kve := range nws { - var n network - err := json.Unmarshal(kve.Value, &n) - if err != nil { - log.Error(err) - continue - } - if prune != nil { - delete(*prune, n.id) - } - n.SetIndex(kve.LastIndex) - c.Lock() - existing, ok := c.networks[n.id] + if c.cfg == nil { c.Unlock() - if ok { - existing.Lock() - // Skip existing network update - if existing.dbIndex != n.Index() { - // Can't use SetIndex() since existing is locked. - existing.dbIndex = n.Index() - existing.dbExists = true - existing.endpointCnt = n.endpointCnt - } - existing.Unlock() - continue - } - - if err = c.newNetworkFromStore(&n); err != nil { - log.Error(err) - } - } -} - -func (c *controller) processEndpointUpdate(ep *endpoint) bool { - nw := ep.network - if nw == nil { - return true - } - nw.Lock() - id := nw.id - nw.Unlock() - - c.Lock() - n, ok := c.networks[id] - c.Unlock() - if !ok { - return true - } - existing, _ := n.EndpointByID(ep.id) - if existing == nil { - return true - } - - ee := existing.(*endpoint) - ee.Lock() - if ee.dbIndex != ep.Index() { - // Can't use SetIndex() because ee is locked. - ee.dbIndex = ep.Index() - ee.dbExists = true - ee.sandboxID = ep.sandboxID - } - ee.Unlock() - - return false -} - -func ensureKeys(key string, cs datastore.DataStore) error { - exists, err := cs.KVStore().Exists(key) - if err != nil { - return err - } - if exists { return nil } - return cs.KVStore().Put(key, []byte{}, nil) -} + scopeConfigs := c.cfg.Scopes + c.Unlock() -func (c *controller) getLocalStoreConfig(cfg *config.Config) *config.DatastoreCfg { - if cfg != nil && cfg.LocalStore.Client.Provider != "" && cfg.LocalStore.Client.Address != "" { - return &cfg.LocalStore + for scope, scfg := range scopeConfigs { + store, err := datastore.NewDataStore(scope, scfg) + if err != nil { + return err + } + c.Lock() + c.stores = append(c.stores, store) + c.Unlock() } - return &defaultLocalStoreConfig + + c.startWatch() + return nil } -func (c *controller) getDataStore(dataScope datastore.DataScope) (dataStore datastore.DataStore) { +func (c *controller) closeStores() { + for _, store := range c.getStores() { + store.Close() + } +} + +func (c *controller) getStore(scope string) datastore.DataStore { c.Lock() - if dataScope == datastore.GlobalScope { - dataStore = c.globalStore - } else if dataScope == datastore.LocalScope { - dataStore = c.localStore + defer c.Unlock() + + for _, store := range c.stores { + if store.Scope() == scope { + return store + } + } + + return nil +} + +func (c *controller) getStores() []datastore.DataStore { + c.Lock() + defer c.Unlock() + + return c.stores +} + +func (c *controller) getNetworkFromStore(nid string) (*network, error) { + for _, store := range c.getStores() { + n := &network{id: nid, ctrlr: c} + err := store.GetObject(datastore.Key(n.Key()...), n) + if err != nil && err != datastore.ErrKeyNotFound { + return nil, fmt.Errorf("could not find network %s: %v", nid, err) + } + + // Continue searching in the next store if the key is not found in this store + if err == datastore.ErrKeyNotFound { + continue + } + + ec := &endpointCnt{n: n} + err = store.GetObject(datastore.Key(ec.Key()...), ec) + if err != nil { + return nil, fmt.Errorf("could not find endpoint count for network %s: %v", n.Name(), err) + } + + n.epCnt = ec + return n, nil + } + + return nil, fmt.Errorf("network %s not found", nid) +} + +func (c *controller) getNetworksFromStore() ([]*network, error) { + var nl []*network + + for _, store := range c.getStores() { + kvol, err := store.List(datastore.Key(datastore.NetworkKeyPrefix), + &network{ctrlr: c}) + if err != nil && err != datastore.ErrKeyNotFound { + return nil, fmt.Errorf("failed to get networks for scope %s: %v", + store.Scope(), err) + } + + // Continue searching in the next store if no keys found in this store + if err == datastore.ErrKeyNotFound { + continue + } + + for _, kvo := range kvol { + n := kvo.(*network) + n.ctrlr = c + + ec := &endpointCnt{n: n} + err = store.GetObject(datastore.Key(ec.Key()...), ec) + if err != nil { + return nil, fmt.Errorf("could not find endpoint count key %s for network %s while listing: %v", datastore.Key(ec.Key()...), n.Name(), err) + } + + n.epCnt = ec + nl = append(nl, n) + } + } + + return nl, nil +} + +func (n *network) getEndpointFromStore(eid string) (*endpoint, error) { + for _, store := range n.ctrlr.getStores() { + ep := &endpoint{id: eid, network: n} + err := store.GetObject(datastore.Key(ep.Key()...), ep) + if err != nil && err != datastore.ErrKeyNotFound { + return nil, fmt.Errorf("could not find endpoint %s: %v", eid, err) + } + + // Continue searching in the next store if the key is not found in this store + if err == datastore.ErrKeyNotFound { + continue + } + + return ep, nil + } + + return nil, fmt.Errorf("endpoint %s not found", eid) +} + +func (n *network) getEndpointsFromStore() ([]*endpoint, error) { + var epl []*endpoint + + tmp := endpoint{network: n} + for _, store := range n.getController().getStores() { + kvol, err := store.List(datastore.Key(tmp.KeyPrefix()...), &endpoint{network: n}) + if err != nil && err != datastore.ErrKeyNotFound { + return nil, + fmt.Errorf("failed to get endpoints for network %s scope %s: %v", + n.Name(), store.Scope(), err) + } + + // Continue searching in the next store if no keys found in this store + if err == datastore.ErrKeyNotFound { + continue + } + + for _, kvo := range kvol { + ep := kvo.(*endpoint) + ep.network = n + epl = append(epl, ep) + } + } + + return epl, nil +} + +func (c *controller) updateToStore(kvObject datastore.KVObject) error { + cs := c.getStore(kvObject.DataScope()) + if cs == nil { + log.Warnf("datastore for scope %s not initialized. kv object %s is not added to the store", kvObject.DataScope(), datastore.Key(kvObject.Key()...)) + return nil + } + + if err := cs.PutObjectAtomic(kvObject); err != nil { + if err == datastore.ErrKeyModified { + return err + } + return fmt.Errorf("failed to update store for object type %T: %v", kvObject, err) + } + + return nil +} + +func (c *controller) deleteFromStore(kvObject datastore.KVObject) error { + cs := c.getStore(kvObject.DataScope()) + if cs == nil { + log.Debugf("datastore for scope %s not initialized. kv object %s is not deleted from datastore", kvObject.DataScope(), datastore.Key(kvObject.Key()...)) + return nil + } + +retry: + if err := cs.DeleteObjectAtomic(kvObject); err != nil { + if err == datastore.ErrKeyModified { + if err := cs.GetObject(datastore.Key(kvObject.Key()...), kvObject); err != nil { + return fmt.Errorf("could not update the kvobject to latest when trying to delete: %v", err) + } + goto retry + } + return err + } + + return nil +} + +type netWatch struct { + localEps map[string]*endpoint + remoteEps map[string]*endpoint + stopCh chan struct{} +} + +func (c *controller) getLocalEps(nw *netWatch) []*endpoint { + c.Lock() + defer c.Unlock() + + var epl []*endpoint + for _, ep := range nw.localEps { + epl = append(epl, ep) + } + + return epl +} + +func (c *controller) watchSvcRecord(ep *endpoint) { + c.watchCh <- ep +} + +func (c *controller) unWatchSvcRecord(ep *endpoint) { + c.unWatchCh <- ep +} + +func (c *controller) networkWatchLoop(nw *netWatch, ep *endpoint, ecCh <-chan datastore.KVObject) { + for { + select { + case <-nw.stopCh: + return + case o := <-ecCh: + ec := o.(*endpointCnt) + + epl, err := ec.n.getEndpointsFromStore() + if err != nil { + break + } + + c.Lock() + var addEp []*endpoint + + delEpMap := make(map[string]*endpoint) + for k, v := range nw.remoteEps { + delEpMap[k] = v + } + + for _, lEp := range epl { + if _, ok := nw.localEps[lEp.ID()]; ok { + continue + } + + if _, ok := nw.remoteEps[lEp.ID()]; ok { + delete(delEpMap, lEp.ID()) + continue + } + + nw.remoteEps[lEp.ID()] = lEp + addEp = append(addEp, lEp) + + } + c.Unlock() + + for _, lEp := range addEp { + ep.getNetwork().updateSvcRecord(lEp, c.getLocalEps(nw), true) + } + + for _, lEp := range delEpMap { + ep.getNetwork().updateSvcRecord(lEp, c.getLocalEps(nw), false) + + } + } + } +} + +func (c *controller) processEndpointCreate(nmap map[string]*netWatch, ep *endpoint) { + c.Lock() + nw, ok := nmap[ep.getNetwork().ID()] + c.Unlock() + + if ok { + // Update the svc db for the local endpoint join right away + ep.getNetwork().updateSvcRecord(ep, c.getLocalEps(nw), true) + + c.Lock() + nw.localEps[ep.ID()] = ep + c.Unlock() + return + } + + nw = &netWatch{ + localEps: make(map[string]*endpoint), + remoteEps: make(map[string]*endpoint), + } + + // Update the svc db for the local endpoint join right away + // Do this before adding this ep to localEps so that we don't + // try to update this ep's container's svc records + ep.getNetwork().updateSvcRecord(ep, c.getLocalEps(nw), true) + + c.Lock() + nw.localEps[ep.ID()] = ep + nmap[ep.getNetwork().ID()] = nw + nw.stopCh = make(chan struct{}) + c.Unlock() + + store := c.getStore(ep.getNetwork().DataScope()) + if store == nil { + return + } + + if !store.Watchable() { + return + } + + ch, err := store.Watch(ep.getNetwork().getEpCnt(), nw.stopCh) + if err != nil { + log.Warnf("Error creating watch for network: %v", err) + return + } + + go c.networkWatchLoop(nw, ep, ch) +} + +func (c *controller) processEndpointDelete(nmap map[string]*netWatch, ep *endpoint) { + c.Lock() + nw, ok := nmap[ep.getNetwork().ID()] + + if ok { + delete(nw.localEps, ep.ID()) + c.Unlock() + + // Update the svc db about local endpoint leave right away + // Do this after we remove this ep from localEps so that we + // don't try to remove this svc record from this ep's container. + ep.getNetwork().updateSvcRecord(ep, c.getLocalEps(nw), false) + + c.Lock() + if len(nw.localEps) == 0 { + close(nw.stopCh) + delete(nmap, ep.getNetwork().ID()) + } } c.Unlock() - return } -func (c *controller) processEndpointsUpdate(eps []*store.KVPair, prune *endpointTable) { - for _, epe := range eps { - var ep endpoint - err := json.Unmarshal(epe.Value, &ep) - if err != nil { - log.Error(err) - continue - } - if prune != nil { - delete(*prune, ep.id) - } - ep.SetIndex(epe.LastIndex) - if nid, err := ep.networkIDFromKey(epe.Key); err != nil { - log.Error(err) - continue - } else { - if n, err := c.NetworkByID(nid); err != nil { - log.Error(err) - continue - } else { - ep.network = n.(*network) - } - } - if c.processEndpointUpdate(&ep) { - err = c.newEndpointFromStore(epe.Key, &ep) - if err != nil { - log.Error(err) - } +func (c *controller) watchLoop(nmap map[string]*netWatch) { + for { + select { + case ep := <-c.watchCh: + c.processEndpointCreate(nmap, ep) + case ep := <-c.unWatchCh: + c.processEndpointDelete(nmap, ep) } } } + +func (c *controller) startWatch() { + c.watchCh = make(chan *endpoint) + c.unWatchCh = make(chan *endpoint) + nmap := make(map[string]*netWatch) + + go c.watchLoop(nmap) +} diff --git a/vendor/src/github.com/docker/libnetwork/types/types.go b/vendor/src/github.com/docker/libnetwork/types/types.go index 9df1af50b1..7ada9643c0 100644 --- a/vendor/src/github.com/docker/libnetwork/types/types.go +++ b/vendor/src/github.com/docker/libnetwork/types/types.go @@ -5,6 +5,7 @@ import ( "bytes" "fmt" "net" + "strconv" "strings" ) @@ -17,11 +18,46 @@ type TransportPort struct { Port uint16 } +// Equal checks if this instance of Transportport is equal to the passed one +func (t *TransportPort) Equal(o *TransportPort) bool { + if t == o { + return true + } + + if o == nil { + return false + } + + if t.Proto != o.Proto || t.Port != o.Port { + return false + } + + return true +} + // GetCopy returns a copy of this TransportPort structure instance func (t *TransportPort) GetCopy() TransportPort { return TransportPort{Proto: t.Proto, Port: t.Port} } +// String returns the TransportPort structure in string form +func (t *TransportPort) String() string { + return fmt.Sprintf("%s/%d", t.Proto.String(), t.Port) +} + +// FromString reads the TransportPort structure from string +func (t *TransportPort) FromString(s string) error { + ps := strings.Split(s, "/") + if len(ps) == 2 { + t.Proto = ParseProtocol(ps[0]) + if p, err := strconv.ParseUint(ps[1], 10, 16); err == nil { + t.Port = uint16(p) + return nil + } + } + return BadRequestErrorf("invalid format for transport port: %s", s) +} + // PortBinding represent a port binding between the container and the host type PortBinding struct { Proto Protocol @@ -68,6 +104,62 @@ func (p *PortBinding) GetCopy() PortBinding { } } +// String return the PortBinding structure in string form +func (p *PortBinding) String() string { + ret := fmt.Sprintf("%s/", p.Proto) + if p.IP != nil { + ret = fmt.Sprintf("%s%s", ret, p.IP.String()) + } + ret = fmt.Sprintf("%s:%d/", ret, p.Port) + if p.HostIP != nil { + ret = fmt.Sprintf("%s%s", ret, p.HostIP.String()) + } + ret = fmt.Sprintf("%s:%d", ret, p.HostPort) + return ret +} + +// FromString reads the TransportPort structure from string +func (p *PortBinding) FromString(s string) error { + ps := strings.Split(s, "/") + if len(ps) != 3 { + return BadRequestErrorf("invalid format for port binding: %s", s) + } + + p.Proto = ParseProtocol(ps[0]) + + var err error + if p.IP, p.Port, err = parseIPPort(ps[1]); err != nil { + return BadRequestErrorf("failed to parse Container IP/Port in port binding: %s", err.Error()) + } + + if p.HostIP, p.HostPort, err = parseIPPort(ps[2]); err != nil { + return BadRequestErrorf("failed to parse Host IP/Port in port binding: %s", err.Error()) + } + + return nil +} + +func parseIPPort(s string) (net.IP, uint16, error) { + pp := strings.Split(s, ":") + if len(pp) != 2 { + return nil, 0, BadRequestErrorf("invalid format: %s", s) + } + + var ip net.IP + if pp[0] != "" { + if ip = net.ParseIP(pp[0]); ip == nil { + return nil, 0, BadRequestErrorf("invalid ip: %s", pp[0]) + } + } + + port, err := strconv.ParseUint(pp[1], 10, 16) + if err != nil { + return nil, 0, BadRequestErrorf("invalid port: %s", pp[1]) + } + + return ip, uint16(port), nil +} + // Equal checks if this instance of PortBinding is equal to the passed one func (p *PortBinding) Equal(o *PortBinding) bool { if p == o { @@ -154,6 +246,9 @@ func ParseProtocol(s string) Protocol { // GetMacCopy returns a copy of the passed MAC address func GetMacCopy(from net.HardwareAddr) net.HardwareAddr { + if from == nil { + return nil + } to := make(net.HardwareAddr, len(from)) copy(to, from) return to @@ -161,6 +256,9 @@ func GetMacCopy(from net.HardwareAddr) net.HardwareAddr { // GetIPCopy returns a copy of the passed IP address func GetIPCopy(from net.IP) net.IP { + if from == nil { + return nil + } to := make(net.IP, len(from)) copy(to, from) return to @@ -222,23 +320,32 @@ func GetMinimalIPNet(nw *net.IPNet) *net.IPNet { var v4inV6MaskPrefix = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} -// GetHostPartIP returns the host portion of the ip address identified by the mask. -// IP address representation is not modified. If address and mask are not compatible -// an error is returned. -func GetHostPartIP(ip net.IP, mask net.IPMask) (net.IP, error) { +// compareIPMask checks if the passed ip and mask are semantically compatible. +// It returns the byte indexes for the address and mask so that caller can +// do bitwise operations without modifying address representation. +func compareIPMask(ip net.IP, mask net.IPMask) (is int, ms int, err error) { // Find the effective starting of address and mask - is := 0 - ms := 0 if len(ip) == net.IPv6len && ip.To4() != nil { is = 12 } if len(ip[is:]) == net.IPv4len && len(mask) == net.IPv6len && bytes.Equal(mask[:12], v4inV6MaskPrefix) { ms = 12 } - // Check if address and mask are semantically compatible if len(ip[is:]) != len(mask[ms:]) { - return nil, fmt.Errorf("cannot compute host portion ip address as ip and mask are not compatible: (%#v, %#v)", ip, mask) + err = fmt.Errorf("ip and mask are not compatible: (%#v, %#v)", ip, mask) + } + return +} + +// GetHostPartIP returns the host portion of the ip address identified by the mask. +// IP address representation is not modified. If address and mask are not compatible +// an error is returned. +func GetHostPartIP(ip net.IP, mask net.IPMask) (net.IP, error) { + // Find the effective starting of address and mask + is, ms, err := compareIPMask(ip, mask) + if err != nil { + return nil, fmt.Errorf("cannot compute host portion ip address because %s", err) } // Compute host portion @@ -250,6 +357,34 @@ func GetHostPartIP(ip net.IP, mask net.IPMask) (net.IP, error) { return out, nil } +// GetBroadcastIP returns the broadcast ip address for the passed network (ip and mask). +// IP address representation is not modified. If address and mask are not compatible +// an error is returned. +func GetBroadcastIP(ip net.IP, mask net.IPMask) (net.IP, error) { + // Find the effective starting of address and mask + is, ms, err := compareIPMask(ip, mask) + if err != nil { + return nil, fmt.Errorf("cannot compute broadcast ip address because %s", err) + } + + // Compute broadcast address + out := GetIPCopy(ip) + for i := 0; i < len(mask[ms:]); i++ { + out[is+i] |= ^mask[ms+i] + } + + return out, nil +} + +// ParseCIDR returns the *net.IPNet represented by the passed CIDR notation +func ParseCIDR(cidr string) (n *net.IPNet, e error) { + var i net.IP + if i, n, e = net.ParseCIDR(cidr); e == nil { + n.IP = i + } + return +} + const ( // NEXTHOP indicates a StaticRoute with an IP next hop. NEXTHOP = iota @@ -278,6 +413,23 @@ func (r *StaticRoute) GetCopy() *StaticRoute { } } +// InterfaceStatistics represents the interface's statistics +type InterfaceStatistics struct { + RxBytes uint64 + RxPackets uint64 + RxErrors uint64 + RxDropped uint64 + TxBytes uint64 + TxPackets uint64 + TxErrors uint64 + TxDropped uint64 +} + +func (is *InterfaceStatistics) String() string { + return fmt.Sprintf("\nRxBytes: %d, RxPackets: %d, RxErrors: %d, RxDropped: %d, TxBytes: %d, TxPackets: %d, TxErrors: %d, TxDropped: %d", + is.RxBytes, is.RxPackets, is.RxErrors, is.RxDropped, is.TxBytes, is.TxPackets, is.TxErrors, is.TxDropped) +} + /****************************** * Well-known Error Interfaces ******************************/