mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
6fb69f0816
With the introduction of a driver generic gossip in libnetwork it is not necessary for drivers to run their own gossip protocol (like what overlay driver is doing currently) but instead rely on the gossip instance run centrally in libnetwork. In order to achieve this, certain enhancements to driver api are needed. This api aims to provide these enhancements. The new api provides a way for drivers to register interest on table names of their choice by returning a list of table names of interest as a response to CreateNetwork. By doing that they will get notified if a CRUD operation happened on the tables of their interest, via the newly added EventNotify call. Drivers themselves can add entries to any table during a Join call by invoking AddTableEntry method any number of times during the Join call. These entries lifetime is the same as the endpoint itself. As soon as the container leaves the endpoint, those entries added by driver during that endpoint's Join call will be automatically removed by libnetwork. This action may trigger notification of such deletion to all driver instances in the cluster who have registered interest in that table's notification. Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
369 lines
8.8 KiB
Go
369 lines
8.8 KiB
Go
package libnetwork
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
|
|
"github.com/docker/libnetwork/driverapi"
|
|
"github.com/docker/libnetwork/types"
|
|
)
|
|
|
|
// EndpointInfo provides an interface to retrieve network resources bound to the endpoint.
|
|
type EndpointInfo interface {
|
|
// Iface returns InterfaceInfo, go interface that can be used
|
|
// to get more information on the interface which was assigned to
|
|
// the endpoint by the driver. This can be used after the
|
|
// endpoint has been created.
|
|
Iface() InterfaceInfo
|
|
|
|
// Gateway returns the IPv4 gateway assigned by the driver.
|
|
// This will only return a valid value if a container has joined the endpoint.
|
|
Gateway() net.IP
|
|
|
|
// GatewayIPv6 returns the IPv6 gateway assigned by the driver.
|
|
// This will only return a valid value if a container has joined the endpoint.
|
|
GatewayIPv6() net.IP
|
|
|
|
// StaticRoutes returns the list of static routes configured by the network
|
|
// driver when the container joins a network
|
|
StaticRoutes() []*types.StaticRoute
|
|
|
|
// Sandbox returns the attached sandbox if there, nil otherwise.
|
|
Sandbox() Sandbox
|
|
}
|
|
|
|
// InterfaceInfo provides an interface to retrieve interface addresses bound to the endpoint.
|
|
type InterfaceInfo interface {
|
|
// MacAddress returns the MAC address assigned to the endpoint.
|
|
MacAddress() net.HardwareAddr
|
|
|
|
// Address returns the IPv4 address assigned to the endpoint.
|
|
Address() *net.IPNet
|
|
|
|
// AddressIPv6 returns the IPv6 address assigned to the endpoint.
|
|
AddressIPv6() *net.IPNet
|
|
}
|
|
|
|
type endpointInterface struct {
|
|
mac net.HardwareAddr
|
|
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{})
|
|
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
|
|
for _, route := range epi.routes {
|
|
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) error {
|
|
var (
|
|
err error
|
|
epMap map[string]interface{}
|
|
)
|
|
if err = json.Unmarshal(b, &epMap); err != nil {
|
|
return err
|
|
}
|
|
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))
|
|
}
|
|
}
|
|
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)
|
|
epi.dstPrefix = epMap["dstPrefix"].(string)
|
|
|
|
rb, _ := json.Marshal(epMap["routes"])
|
|
var routes []string
|
|
json.Unmarshal(rb, &routes)
|
|
epi.routes = make([]*net.IPNet, 0)
|
|
for _, route := range routes {
|
|
ip, ipr, err := net.ParseCIDR(route)
|
|
if err == nil {
|
|
ipr.IP = ip
|
|
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
|
|
}
|
|
|
|
type endpointJoinInfo struct {
|
|
gw net.IP
|
|
gw6 net.IP
|
|
StaticRoutes []*types.StaticRoute
|
|
disableGatewayService bool
|
|
}
|
|
|
|
func (ep *endpoint) Info() EndpointInfo {
|
|
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
|
|
}
|
|
|
|
if epi := sb.getEndpoint(ep.ID()); epi != nil {
|
|
return epi
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ep *endpoint) DriverInfo() (map[string]interface{}, error) {
|
|
ep, err := ep.retrieveFromStore()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if sb, ok := ep.getSandbox(); ok {
|
|
if gwep := sb.getEndpointInGWNetwork(); gwep != nil && gwep.ID() != ep.ID() {
|
|
return gwep.DriverInfo()
|
|
}
|
|
}
|
|
|
|
n, err := ep.getNetworkFromStore()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not find network in store for driver info: %v", err)
|
|
}
|
|
|
|
driver, err := n.driver(true)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get driver info: %v", err)
|
|
}
|
|
|
|
return driver.EndpointOperInfo(n.ID(), ep.ID())
|
|
}
|
|
|
|
func (ep *endpoint) Iface() InterfaceInfo {
|
|
ep.Lock()
|
|
defer ep.Unlock()
|
|
|
|
if ep.iface != nil {
|
|
return ep.iface
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ep *endpoint) Interface() driverapi.InterfaceInfo {
|
|
ep.Lock()
|
|
defer ep.Unlock()
|
|
|
|
if ep.iface != nil {
|
|
return ep.iface
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
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)
|
|
}
|
|
if mac == nil {
|
|
return types.BadRequestErrorf("tried to set nil MAC address to endpoint interface")
|
|
}
|
|
epi.mac = types.GetMacCopy(mac)
|
|
return nil
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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) AddressIPv6() *net.IPNet {
|
|
return types.GetIPNetCopy(epi.addrv6)
|
|
}
|
|
|
|
func (epi *endpointInterface) SetNames(srcName string, dstPrefix string) error {
|
|
epi.srcName = srcName
|
|
epi.dstPrefix = dstPrefix
|
|
return nil
|
|
}
|
|
|
|
func (ep *endpoint) InterfaceName() driverapi.InterfaceNameInfo {
|
|
ep.Lock()
|
|
defer ep.Unlock()
|
|
|
|
if ep.iface != nil {
|
|
return ep.iface
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ep *endpoint) AddStaticRoute(destination *net.IPNet, routeType int, nextHop net.IP) error {
|
|
ep.Lock()
|
|
defer ep.Unlock()
|
|
|
|
r := types.StaticRoute{Destination: destination, RouteType: routeType, NextHop: nextHop}
|
|
|
|
if routeType == types.NEXTHOP {
|
|
// If the route specifies a next-hop, then it's loosely routed (i.e. not bound to a particular interface).
|
|
ep.joinInfo.StaticRoutes = append(ep.joinInfo.StaticRoutes, &r)
|
|
} else {
|
|
// If the route doesn't specify a next-hop, it must be a connected route, bound to an interface.
|
|
ep.iface.routes = append(ep.iface.routes, r.Destination)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (ep *endpoint) AddTableEntry(tableName, key string, value []byte) error {
|
|
return nil
|
|
}
|
|
|
|
func (ep *endpoint) Sandbox() Sandbox {
|
|
cnt, ok := ep.getSandbox()
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return cnt
|
|
}
|
|
|
|
func (ep *endpoint) StaticRoutes() []*types.StaticRoute {
|
|
ep.Lock()
|
|
defer ep.Unlock()
|
|
|
|
if ep.joinInfo == nil {
|
|
return nil
|
|
}
|
|
|
|
return ep.joinInfo.StaticRoutes
|
|
}
|
|
|
|
func (ep *endpoint) Gateway() net.IP {
|
|
ep.Lock()
|
|
defer ep.Unlock()
|
|
|
|
if ep.joinInfo == nil {
|
|
return net.IP{}
|
|
}
|
|
|
|
return types.GetIPCopy(ep.joinInfo.gw)
|
|
}
|
|
|
|
func (ep *endpoint) GatewayIPv6() net.IP {
|
|
ep.Lock()
|
|
defer ep.Unlock()
|
|
|
|
if ep.joinInfo == nil {
|
|
return net.IP{}
|
|
}
|
|
|
|
return types.GetIPCopy(ep.joinInfo.gw6)
|
|
}
|
|
|
|
func (ep *endpoint) SetGateway(gw net.IP) error {
|
|
ep.Lock()
|
|
defer ep.Unlock()
|
|
|
|
ep.joinInfo.gw = types.GetIPCopy(gw)
|
|
return nil
|
|
}
|
|
|
|
func (ep *endpoint) SetGatewayIPv6(gw6 net.IP) error {
|
|
ep.Lock()
|
|
defer ep.Unlock()
|
|
|
|
ep.joinInfo.gw6 = types.GetIPCopy(gw6)
|
|
return nil
|
|
}
|
|
|
|
func (ep *endpoint) retrieveFromStore() (*endpoint, error) {
|
|
n, err := ep.getNetworkFromStore()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not find network in store to get latest endpoint %s: %v", ep.Name(), err)
|
|
}
|
|
return n.getEndpointFromStore(ep.ID())
|
|
}
|
|
|
|
func (ep *endpoint) DisableGatewayService() {
|
|
ep.Lock()
|
|
defer ep.Unlock()
|
|
|
|
ep.joinInfo.disableGatewayService = true
|
|
}
|