- Added support for Join/Leave methods to Endpoint.
- Removed sandbox key argument for CreateEndpoint. - Refactored bridge driver code to remove sandbox key. - Fixed bridge driver code for gaps in ipv6 behavior observed during docker integration. - Updated test code, readme code, README.md according api change. - Fixed some sandbox issues while testing docker ipv6 integration. Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
This commit is contained in:
parent
144a42267b
commit
e2d8531a40
14 changed files with 443 additions and 175 deletions
|
@ -19,54 +19,33 @@ Please refer to the [roadmap](ROADMAP.md) for more information.
|
|||
|
||||
There are many networking solutions available to suit a broad range of use-cases. libnetwork uses a driver / plugin model to support all of these solutions while abstracting the complexity of the driver implementations by exposing a simple and consistent Network Model to users.
|
||||
|
||||
|
||||
```go
|
||||
// Create a new controller instance
|
||||
controller := libnetwork.New()
|
||||
// Create a new controller instance
|
||||
controller := libnetwork.New()
|
||||
|
||||
// This option is only needed for in-tree drivers. Plugins(in future) will get
|
||||
// their options through plugin infrastructure.
|
||||
option := options.Generic{}
|
||||
driver, err := controller.NewNetworkDriver("bridge", option)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// This option is only needed for in-tree drivers. Plugins(in future) will get
|
||||
// their options through plugin infrastructure.
|
||||
option := options.Generic{}
|
||||
err := controller.NewNetworkDriver("bridge", option)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
netOptions := options.Generic{}
|
||||
// Create a network for containers to join.
|
||||
network, err := controller.NewNetwork(driver, "network1", netOptions)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
netOptions := options.Generic{}
|
||||
// Create a network for containers to join.
|
||||
network, err := controller.NewNetwork("bridge", "network1", netOptions)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// For a new container: create a sandbox instance (providing a unique key).
|
||||
// For linux it is a filesystem path
|
||||
networkPath := "/var/lib/docker/.../4d23e"
|
||||
networkNamespace, err := sandbox.NewSandbox(networkPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// For each new container: allocate IP and interfaces. The returned network
|
||||
// settings will be used for container infos (inspect and such), as well as
|
||||
// iptables rules for port publishing. This info is contained or accessible
|
||||
// from the returned endpoint.
|
||||
ep, err := network.CreateEndpoint("Endpoint1", networkNamespace.Key(), "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Add interfaces to the namespace.
|
||||
sinfo := ep.SandboxInfo()
|
||||
for _, iface := range sinfo.Interfaces {
|
||||
if err := networkNamespace.AddInterface(iface); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Set the gateway IP
|
||||
if err := networkNamespace.SetGateway(sinfo.Gateway); err != nil {
|
||||
return
|
||||
}
|
||||
// For each new container: allocate IP and interfaces. The returned network
|
||||
// settings will be used for container infos (inspect and such), as well as
|
||||
// iptables rules for port publishing.
|
||||
ep, err := network.CreateEndpoint("Endpoint1", nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
## Future
|
||||
|
|
|
@ -12,7 +12,7 @@ To suggest changes to the roadmap, including additions, please write the change
|
|||
#### Concepts
|
||||
|
||||
1. Sandbox: An isolated environment. This is more or less a standard docker container.
|
||||
2. Endpoint: An addressable endpoint used for communication over a specific network. Endpoints join exactly one network and are expected to create a method of network communication for a container. Endpoints are garbage collected when they no longer belong to any Sandboxes. Example : veth pair
|
||||
2. Endpoint: An addressable endpoint used for communication over a specific network. Endpoints join exactly one network and are expected to create a method of network communication for a container. Example : veth pair
|
||||
3. Network: A collection of endpoints that are able to communicate to each other. Networks are intended to be isolated from each other and to not cross communicate.
|
||||
|
||||
#### axioms
|
||||
|
|
|
@ -3,7 +3,6 @@ package main
|
|||
import (
|
||||
"github.com/docker/libnetwork"
|
||||
"github.com/docker/libnetwork/pkg/options"
|
||||
"github.com/docker/libnetwork/sandbox"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -25,33 +24,20 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
// For a new container: create a sandbox instance (providing a unique key).
|
||||
// For linux it is a filesystem path
|
||||
networkPath := "/var/lib/docker/.../4d23e"
|
||||
networkNamespace, err := sandbox.NewSandbox(networkPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// For each new container: allocate IP and interfaces. The returned network
|
||||
// settings will be used for container infos (inspect and such), as well as
|
||||
// iptables rules for port publishing. This info is contained or accessible
|
||||
// from the returned endpoint.
|
||||
ep, err := network.CreateEndpoint("Endpoint1", networkNamespace.Key(), nil)
|
||||
ep, err := network.CreateEndpoint("Endpoint1", nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Add interfaces to the namespace.
|
||||
sinfo := ep.SandboxInfo()
|
||||
for _, iface := range sinfo.Interfaces {
|
||||
if err := networkNamespace.AddInterface(iface); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Set the gateway IP
|
||||
if err := networkNamespace.SetGateway(sinfo.Gateway); err != nil {
|
||||
// A container can join the endpoint by providing the container ID to the join
|
||||
// api which returns the sandbox key which can be used to access the sandbox
|
||||
// created for the container during join.
|
||||
_, err = ep.Join("container1")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,10 +31,10 @@ type Driver interface {
|
|||
DeleteNetwork(nid types.UUID) error
|
||||
|
||||
// CreateEndpoint invokes the driver method to create an endpoint
|
||||
// passing the network id, endpoint id, sandbox key and driver
|
||||
// passing the network id, endpoint id and driver
|
||||
// specific config. The config mechanism will eventually be replaced
|
||||
// with labels which are yet to be introduced.
|
||||
CreateEndpoint(nid, eid types.UUID, key string, config interface{}) (*sandbox.Info, error)
|
||||
CreateEndpoint(nid, eid types.UUID, config interface{}) (*sandbox.Info, error)
|
||||
|
||||
// DeleteEndpoint invokes the driver method to delete an endpoint
|
||||
// passing the network id and endpoint id.
|
||||
|
|
|
@ -57,8 +57,8 @@ type bridgeEndpoint struct {
|
|||
|
||||
type bridgeNetwork struct {
|
||||
id types.UUID
|
||||
bridge *bridgeInterface // The bridge's L3 interface
|
||||
endpoints map[string]*bridgeEndpoint // key: sandbox id
|
||||
bridge *bridgeInterface // The bridge's L3 interface
|
||||
endpoints map[types.UUID]*bridgeEndpoint // key: endpoint id
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
|
@ -118,21 +118,19 @@ func (c *Configuration) Validate() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (n *bridgeNetwork) getEndpoint(eid types.UUID) (string, *bridgeEndpoint, error) {
|
||||
func (n *bridgeNetwork) getEndpoint(eid types.UUID) (*bridgeEndpoint, error) {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
if eid == "" {
|
||||
return "", nil, InvalidEndpointIDError(eid)
|
||||
return nil, InvalidEndpointIDError(eid)
|
||||
}
|
||||
|
||||
for sk, ep := range n.endpoints {
|
||||
if ep.id == eid {
|
||||
return sk, ep, nil
|
||||
}
|
||||
if ep, ok := n.endpoints[eid]; ok {
|
||||
return ep, nil
|
||||
}
|
||||
|
||||
return "", nil, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *driver) Config(option interface{}) error {
|
||||
|
@ -184,7 +182,7 @@ func (d *driver) CreateNetwork(id types.UUID, option interface{}) error {
|
|||
}
|
||||
|
||||
// Create and set network handler in driver
|
||||
d.network = &bridgeNetwork{id: id, endpoints: make(map[string]*bridgeEndpoint)}
|
||||
d.network = &bridgeNetwork{id: id, endpoints: make(map[types.UUID]*bridgeEndpoint)}
|
||||
d.Unlock()
|
||||
|
||||
// On failure make sure to reset driver network handler to nil
|
||||
|
@ -299,7 +297,7 @@ func (d *driver) DeleteNetwork(nid types.UUID) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (d *driver) CreateEndpoint(nid, eid types.UUID, sboxKey string, epOptions interface{}) (*sandbox.Info, error) {
|
||||
func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions interface{}) (*sandbox.Info, error) {
|
||||
var (
|
||||
ipv6Addr *net.IPNet
|
||||
err error
|
||||
|
@ -323,7 +321,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, sboxKey string, epOptions i
|
|||
n.Unlock()
|
||||
|
||||
// Check if endpoint id is good and retrieve correspondent endpoint
|
||||
_, ep, err := n.getEndpoint(eid)
|
||||
ep, err := n.getEndpoint(eid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -333,18 +331,6 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, sboxKey string, epOptions i
|
|||
return nil, driverapi.ErrEndpointExists
|
||||
}
|
||||
|
||||
// Check if valid sandbox key
|
||||
if sboxKey == "" {
|
||||
return nil, InvalidSandboxIDError(sboxKey)
|
||||
}
|
||||
|
||||
// Check if endpoint already exists for this sandbox
|
||||
n.Lock()
|
||||
if _, ok := n.endpoints[sboxKey]; ok {
|
||||
n.Unlock()
|
||||
return nil, driverapi.ErrEndpointExists
|
||||
}
|
||||
|
||||
// Try to convert the options to endpoint configuration
|
||||
epConfig, err := parseEndpointOptions(epOptions)
|
||||
if err != nil {
|
||||
|
@ -353,15 +339,16 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, sboxKey string, epOptions i
|
|||
}
|
||||
|
||||
// Create and add the endpoint
|
||||
n.Lock()
|
||||
endpoint := &bridgeEndpoint{id: eid, config: epConfig}
|
||||
n.endpoints[sboxKey] = endpoint
|
||||
n.endpoints[eid] = endpoint
|
||||
n.Unlock()
|
||||
|
||||
// On failure make sure to remove the endpoint
|
||||
defer func() {
|
||||
if err != nil {
|
||||
n.Lock()
|
||||
delete(n.endpoints, sboxKey)
|
||||
delete(n.endpoints, eid)
|
||||
n.Unlock()
|
||||
}
|
||||
}()
|
||||
|
@ -408,12 +395,15 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, sboxKey string, epOptions i
|
|||
}
|
||||
}()
|
||||
|
||||
mac := netutils.GenerateRandomMAC()
|
||||
// Add user specified attributes
|
||||
if epConfig != nil && epConfig.MacAddress != nil {
|
||||
err = netlink.LinkSetHardwareAddr(sbox, epConfig.MacAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mac = epConfig.MacAddress
|
||||
}
|
||||
|
||||
err = netlink.LinkSetHardwareAddr(sbox, mac)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add bridge inherited attributes to pipe interfaces
|
||||
|
@ -443,11 +433,28 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, sboxKey string, epOptions i
|
|||
|
||||
// v6 address for the sandbox side pipe interface
|
||||
if config.EnableIPv6 {
|
||||
ip6, err := ipAllocator.RequestIP(n.bridge.bridgeIPv6, nil)
|
||||
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 {
|
||||
ip6[i+10] = h
|
||||
}
|
||||
}
|
||||
|
||||
ip6, err := ipAllocator.RequestIP(network, ip6)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ipv6Addr = &net.IPNet{IP: ip6, Mask: n.bridge.bridgeIPv6.Mask}
|
||||
|
||||
ipv6Addr = &net.IPNet{IP: ip6, Mask: network.Mask}
|
||||
}
|
||||
|
||||
// Store the sandbox side pipe interface
|
||||
|
@ -494,7 +501,7 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
|
|||
n.Unlock()
|
||||
|
||||
// Check endpoint id and if an endpoint is actually there
|
||||
sboxKey, ep, err := n.getEndpoint(eid)
|
||||
ep, err := n.getEndpoint(eid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -504,7 +511,7 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
|
|||
|
||||
// Remove it
|
||||
n.Lock()
|
||||
delete(n.endpoints, sboxKey)
|
||||
delete(n.endpoints, eid)
|
||||
n.Unlock()
|
||||
|
||||
// On failure make sure to set back ep in n.endpoints, but only
|
||||
|
@ -512,8 +519,8 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
|
|||
defer func() {
|
||||
if err != nil {
|
||||
n.Lock()
|
||||
if _, ok := n.endpoints[sboxKey]; !ok {
|
||||
n.endpoints[sboxKey] = ep
|
||||
if _, ok := n.endpoints[eid]; !ok {
|
||||
n.endpoints[eid] = ep
|
||||
}
|
||||
n.Unlock()
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ func TestCreateLinkWithOptions(t *testing.T) {
|
|||
mac := net.HardwareAddr([]byte{0x1e, 0x67, 0x66, 0x44, 0x55, 0x66})
|
||||
epConf := &EndpointConfiguration{MacAddress: mac}
|
||||
|
||||
sinfo, err := d.CreateEndpoint("net1", "ep", "s1", epConf)
|
||||
sinfo, err := d.CreateEndpoint("net1", "ep", epConf)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a link: %s", err.Error())
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ func TestSetDefaultGw(t *testing.T) {
|
|||
t.Fatalf("Failed to create bridge: %v", err)
|
||||
}
|
||||
|
||||
sinfo, err := d.CreateEndpoint("dummy", "ep", "sb2", nil)
|
||||
sinfo, err := d.CreateEndpoint("dummy", "ep", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create endpoint: %v", err)
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ func TestLinkCreate(t *testing.T) {
|
|||
t.Fatalf("Failed to create bridge: %v", err)
|
||||
}
|
||||
|
||||
sinfo, err := d.CreateEndpoint("dummy", "", "sb1", nil)
|
||||
sinfo, err := d.CreateEndpoint("dummy", "", nil)
|
||||
if err != nil {
|
||||
if _, ok := err.(InvalidEndpointIDError); !ok {
|
||||
t.Fatalf("Failed with a wrong error :%s", err.Error())
|
||||
|
@ -37,17 +37,8 @@ func TestLinkCreate(t *testing.T) {
|
|||
t.Fatalf("Failed to detect invalid config")
|
||||
}
|
||||
|
||||
sinfo, err = d.CreateEndpoint("dummy", "ep", "", nil)
|
||||
if err != nil {
|
||||
if _, ok := err.(InvalidSandboxIDError); !ok {
|
||||
t.Fatalf("Failed with a wrong error :%s", err.Error())
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("Failed to detect invalid config")
|
||||
}
|
||||
|
||||
// Good endpoint creation
|
||||
sinfo, err = d.CreateEndpoint("dummy", "ep", "cc", nil)
|
||||
sinfo, err = d.CreateEndpoint("dummy", "ep", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a link: %s", err.Error())
|
||||
}
|
||||
|
@ -63,16 +54,11 @@ func TestLinkCreate(t *testing.T) {
|
|||
// TODO: if we could get peer name from (sboxLnk.(*netlink.Veth)).PeerName
|
||||
// then we could check the MTU on hostLnk as well.
|
||||
|
||||
_, err = d.CreateEndpoint("dummy", "ep", "cc2", nil)
|
||||
_, err = d.CreateEndpoint("dummy", "ep", nil)
|
||||
if err == nil {
|
||||
t.Fatalf("Failed to detect duplicate endpoint id on same network")
|
||||
}
|
||||
|
||||
_, err = d.CreateEndpoint("dummy", "ep2", "cc", nil)
|
||||
if err == nil {
|
||||
t.Fatalf("Failed to detect addition of more than one endpoint to same sandbox")
|
||||
}
|
||||
|
||||
interfaces := sinfo.Interfaces
|
||||
if len(interfaces) != 1 {
|
||||
t.Fatalf("Expected exactly one interface. Instead got %d interface(s)", len(interfaces))
|
||||
|
@ -125,12 +111,12 @@ func TestLinkCreateTwo(t *testing.T) {
|
|||
t.Fatalf("Failed to create bridge: %v", err)
|
||||
}
|
||||
|
||||
_, err = d.CreateEndpoint("dummy", "ep", "s1", nil)
|
||||
_, err = d.CreateEndpoint("dummy", "ep", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a link: %s", err.Error())
|
||||
}
|
||||
|
||||
_, err = d.CreateEndpoint("dummy", "ep", "s1", nil)
|
||||
_, err = d.CreateEndpoint("dummy", "ep", nil)
|
||||
if err != nil {
|
||||
if err != driverapi.ErrEndpointExists {
|
||||
t.Fatalf("Failed with a wrong error :%s", err.Error())
|
||||
|
@ -155,7 +141,7 @@ func TestLinkCreateNoEnableIPv6(t *testing.T) {
|
|||
t.Fatalf("Failed to create bridge: %v", err)
|
||||
}
|
||||
|
||||
sinfo, err := d.CreateEndpoint("dummy", "ep", "sb2", nil)
|
||||
sinfo, err := d.CreateEndpoint("dummy", "ep", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a link: %s", err.Error())
|
||||
}
|
||||
|
@ -186,7 +172,7 @@ func TestLinkDelete(t *testing.T) {
|
|||
t.Fatalf("Failed to create bridge: %v", err)
|
||||
}
|
||||
|
||||
_, err = d.CreateEndpoint("dummy", "ep1", "s1", nil)
|
||||
_, err = d.CreateEndpoint("dummy", "ep1", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a link: %s", err.Error())
|
||||
}
|
||||
|
|
|
@ -29,8 +29,15 @@ func setupBridgeIPv6(config *Configuration, i *bridgeInterface) error {
|
|||
return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err)
|
||||
}
|
||||
|
||||
if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: bridgeIPv6}); err != nil {
|
||||
return &IPv6AddrAddError{ip: bridgeIPv6, err: err}
|
||||
_, addrsv6, err := i.addresses()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(addrsv6) == 0 {
|
||||
if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: bridgeIPv6}); err != nil {
|
||||
return &IPv6AddrAddError{ip: bridgeIPv6, err: err}
|
||||
}
|
||||
}
|
||||
|
||||
// Store bridge network and default gateway
|
||||
|
|
|
@ -12,6 +12,9 @@ var (
|
|||
// ErrInvalidNetworkDriver is returned if an invalid driver
|
||||
// instance is passed.
|
||||
ErrInvalidNetworkDriver = errors.New("invalid driver bound to network")
|
||||
// ErrInvalidJoin is returned if a join is attempted on an endpoint
|
||||
// which already has a container joined.
|
||||
ErrInvalidJoin = errors.New("A container has already joined the endpoint")
|
||||
)
|
||||
|
||||
// NetworkTypeError type is returned when the network type string is not
|
||||
|
@ -61,3 +64,11 @@ type UnknownEndpointError struct {
|
|||
func (uee *UnknownEndpointError) Error() string {
|
||||
return fmt.Sprintf("unknown endpoint %s id %s", uee.name, uee.id)
|
||||
}
|
||||
|
||||
// InvalidContainerIDError is returned when an invalid container id is passed
|
||||
// in Join/Leave
|
||||
type InvalidContainerIDError string
|
||||
|
||||
func (id InvalidContainerIDError) Error() string {
|
||||
return fmt.Sprintf("invalid container id %s", string(id))
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ func TestBridge(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ep, err := network.CreateEndpoint("testep", "sb1", nil)
|
||||
ep, err := network.CreateEndpoint("testep", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -199,7 +199,7 @@ func TestDeleteNetworkWithActiveEndpoints(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ep, err := network.CreateEndpoint("testep", "sb2", nil)
|
||||
ep, err := network.CreateEndpoint("testep", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -267,7 +267,7 @@ func TestUnknownEndpoint(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ep, err := network.CreateEndpoint("testep", "sb1", nil)
|
||||
ep, err := network.CreateEndpoint("testep", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -308,11 +308,11 @@ func TestNetworkEndpointsWalkers(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ep11, err := net1.CreateEndpoint("ep11", "sbox1", nil)
|
||||
ep11, err := net1.CreateEndpoint("ep11", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ep12, err := net1.CreateEndpoint("ep12", "sbox2", nil)
|
||||
ep12, err := net1.CreateEndpoint("ep12", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -434,11 +434,11 @@ func TestNetworkQuery(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ep11, err := net1.CreateEndpoint("ep11", "sbox1", nil)
|
||||
ep11, err := net1.CreateEndpoint("ep11", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ep12, err := net1.CreateEndpoint("ep12", "sbox2", nil)
|
||||
ep12, err := net1.CreateEndpoint("ep12", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -469,3 +469,136 @@ func TestNetworkQuery(t *testing.T) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
const containerID = "valid_container"
|
||||
|
||||
func TestEndpointJoin(t *testing.T) {
|
||||
defer netutils.SetupTestNetNS(t)()
|
||||
|
||||
n, err := createTestNetwork("bridge", "testnetwork", options.Generic{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ep, err := n.CreateEndpoint("ep1", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = ep.Join(containerID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ep.Leave(containerID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEndpointJoinInvalidContainerId(t *testing.T) {
|
||||
defer netutils.SetupTestNetNS(t)()
|
||||
|
||||
n, err := createTestNetwork("bridge", "testnetwork", options.Generic{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ep, err := n.CreateEndpoint("ep1", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = ep.Join("")
|
||||
if err == nil {
|
||||
t.Fatal("Expected to fail join with empty container id string")
|
||||
}
|
||||
|
||||
if _, ok := err.(libnetwork.InvalidContainerIDError); !ok {
|
||||
t.Fatalf("Failed for unexpected reason: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEndpointMultipleJoins(t *testing.T) {
|
||||
defer netutils.SetupTestNetNS(t)()
|
||||
|
||||
n, err := createTestNetwork("bridge", "testnetwork", options.Generic{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ep, err := n.CreateEndpoint("ep1", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = ep.Join(containerID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = ep.Join("container2")
|
||||
if err == nil {
|
||||
t.Fatal("Expected to fail multiple joins for the same endpoint")
|
||||
}
|
||||
|
||||
if err != libnetwork.ErrInvalidJoin {
|
||||
t.Fatalf("Failed for unexpected reason: %v", err)
|
||||
}
|
||||
|
||||
err = ep.Leave(containerID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEndpointInvalidLeave(t *testing.T) {
|
||||
defer netutils.SetupTestNetNS(t)()
|
||||
|
||||
n, err := createTestNetwork("bridge", "testnetwork", options.Generic{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ep, err := n.CreateEndpoint("ep1", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ep.Leave(containerID)
|
||||
if err == nil {
|
||||
t.Fatal("Expected to fail leave from an endpoint which has no active join")
|
||||
}
|
||||
|
||||
if _, ok := err.(libnetwork.InvalidContainerIDError); !ok {
|
||||
t.Fatalf("Failed for unexpected reason: %v", err)
|
||||
}
|
||||
|
||||
_, err = ep.Join(containerID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = ep.Leave("")
|
||||
if err == nil {
|
||||
t.Fatal("Expected to fail leave with empty container id")
|
||||
}
|
||||
|
||||
if _, ok := err.(libnetwork.InvalidContainerIDError); !ok {
|
||||
t.Fatalf("Failed for unexpected reason: %v", err)
|
||||
}
|
||||
|
||||
err = ep.Leave("container2")
|
||||
if err == nil {
|
||||
t.Fatal("Expected to fail leave with wrong container id")
|
||||
}
|
||||
|
||||
if _, ok := err.(libnetwork.InvalidContainerIDError); !ok {
|
||||
t.Fatalf("Failed for unexpected reason: %v", err)
|
||||
}
|
||||
|
||||
err = ep.Leave(containerID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,39 +2,40 @@
|
|||
Package libnetwork provides the basic functionality and extension points to
|
||||
create network namespaces and allocate interfaces for containers to use.
|
||||
|
||||
// Create a new controller instance
|
||||
controller := libnetwork.New()
|
||||
// Create a new controller instance
|
||||
controller := libnetwork.New()
|
||||
|
||||
// This option is only needed for in-tree drivers. Plugins(in future) will get
|
||||
// their options through plugin infrastructure.
|
||||
option := options.Generic{}
|
||||
err := controller.NewNetworkDriver("bridge", option)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// Select and configure the network driver
|
||||
networkType := "bridge"
|
||||
option := options.Generic{}
|
||||
err := controller.ConfigureNetworkDriver(networkType, option)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
netOptions := options.Generic{}
|
||||
// Create a network for containers to join.
|
||||
network, err := controller.NewNetwork("bridge", "network1", netOptions)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
netOptions := options.Generic{}
|
||||
// Create a network for containers to join.
|
||||
network, err := controller.NewNetwork(networkType, "network1", netOptions)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// For a new container: create a sandbox instance (providing a unique key).
|
||||
// For linux it is a filesystem path
|
||||
networkPath := "/var/lib/docker/.../4d23e"
|
||||
networkNamespace, err := sandbox.NewSandbox(networkPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// For each new container: allocate IP and interfaces. The returned network
|
||||
// settings will be used for container infos (inspect and such), as well as
|
||||
// iptables rules for port publishing. This info is contained or accessible
|
||||
// from the returned endpoint.
|
||||
ep, err := network.CreateEndpoint("Endpoint1", nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// For each new container: allocate IP and interfaces. The returned network
|
||||
// settings will be used for container infos (inspect and such), as well as
|
||||
// iptables rules for port publishing.
|
||||
ep, err := network.CreateEndpoint("Endpoint1", networkNamespace.Key(), nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// A container can join the endpoint by providing the container ID to the join
|
||||
// api which returns the sandbox key which can be used to access the sandbox
|
||||
// created for the container during join.
|
||||
_, err = ep.Join("container1")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
*/
|
||||
package libnetwork
|
||||
|
||||
|
@ -85,7 +86,7 @@ type Network interface {
|
|||
// Create a new endpoint to this network symbolically identified by the
|
||||
// specified unique name. The options parameter carry driver specific options.
|
||||
// Labels support will be added in the near future.
|
||||
CreateEndpoint(name string, sboxKey string, options interface{}) (Endpoint, error)
|
||||
CreateEndpoint(name string, options interface{}) (Endpoint, error)
|
||||
|
||||
// Delete the network.
|
||||
Delete() error
|
||||
|
@ -118,6 +119,15 @@ type Endpoint interface {
|
|||
// Network returns the name of the network to which this endpoint is attached.
|
||||
Network() string
|
||||
|
||||
// Join creates a new sandbox for the given container ID and populates the
|
||||
// network resources allocated for the endpoint and joins the sandbox to
|
||||
// the endpoint. It returns the sandbox key to the caller
|
||||
Join(containerID string) (string, error)
|
||||
|
||||
// Leave removes the sandbox associated with container ID and detaches
|
||||
// the network resources populated in the sandbox
|
||||
Leave(containerID string) error
|
||||
|
||||
// SandboxInfo returns the sandbox information for this endpoint.
|
||||
SandboxInfo() *sandbox.Info
|
||||
|
||||
|
@ -144,20 +154,29 @@ type endpoint struct {
|
|||
id types.UUID
|
||||
network *network
|
||||
sandboxInfo *sandbox.Info
|
||||
sandBox sandbox.Sandbox
|
||||
containerID string
|
||||
}
|
||||
|
||||
type sandboxData struct {
|
||||
sandbox sandbox.Sandbox
|
||||
refCnt int
|
||||
}
|
||||
|
||||
type networkTable map[types.UUID]*network
|
||||
type endpointTable map[types.UUID]*endpoint
|
||||
type sandboxTable map[string]sandboxData
|
||||
|
||||
type controller struct {
|
||||
networks networkTable
|
||||
drivers driverTable
|
||||
networks networkTable
|
||||
drivers driverTable
|
||||
sandboxes sandboxTable
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// New creates a new instance of network controller.
|
||||
func New() NetworkController {
|
||||
return &controller{networkTable{}, enumerateDrivers(), sync.Mutex{}}
|
||||
return &controller{networkTable{}, enumerateDrivers(), sandboxTable{}, sync.Mutex{}}
|
||||
}
|
||||
|
||||
func (c *controller) ConfigureNetworkDriver(networkType string, options interface{}) error {
|
||||
|
@ -256,6 +275,51 @@ func (c *controller) NetworkByID(id string) Network {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) sandboxAdd(key string) (sandbox.Sandbox, error) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
sData, ok := c.sandboxes[key]
|
||||
if !ok {
|
||||
sb, err := sandbox.NewSandbox(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sData = sandboxData{sandbox: sb, refCnt: 1}
|
||||
c.sandboxes[key] = sData
|
||||
return sData.sandbox, nil
|
||||
}
|
||||
|
||||
sData.refCnt++
|
||||
return sData.sandbox, nil
|
||||
}
|
||||
|
||||
func (c *controller) sandboxRm(key string) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
sData := c.sandboxes[key]
|
||||
sData.refCnt--
|
||||
|
||||
if sData.refCnt == 0 {
|
||||
sData.sandbox.Destroy()
|
||||
delete(c.sandboxes, key)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *controller) sandboxGet(key string) sandbox.Sandbox {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
sData, ok := c.sandboxes[key]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return sData.sandbox
|
||||
}
|
||||
|
||||
func (n *network) Name() string {
|
||||
return n.name
|
||||
}
|
||||
|
@ -304,13 +368,13 @@ func (n *network) Delete() error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (n *network) CreateEndpoint(name string, sboxKey string, options interface{}) (Endpoint, error) {
|
||||
func (n *network) CreateEndpoint(name string, options interface{}) (Endpoint, error) {
|
||||
ep := &endpoint{name: name}
|
||||
ep.id = types.UUID(stringid.GenerateRandomID())
|
||||
ep.network = n
|
||||
|
||||
d := n.driver
|
||||
sinfo, err := d.CreateEndpoint(n.id, ep.id, sboxKey, options)
|
||||
sinfo, err := d.CreateEndpoint(n.id, ep.id, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -387,6 +451,60 @@ func (ep *endpoint) SandboxInfo() *sandbox.Info {
|
|||
return ep.sandboxInfo.GetCopy()
|
||||
}
|
||||
|
||||
func (ep *endpoint) Join(containerID string) (string, error) {
|
||||
if containerID == "" {
|
||||
return "", InvalidContainerIDError(containerID)
|
||||
}
|
||||
|
||||
if ep.containerID != "" {
|
||||
return "", ErrInvalidJoin
|
||||
}
|
||||
|
||||
sboxKey := sandbox.GenerateKey(containerID)
|
||||
sb, err := ep.network.ctrlr.sandboxAdd(sboxKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
ep.network.ctrlr.sandboxRm(sboxKey)
|
||||
}
|
||||
}()
|
||||
|
||||
sinfo := ep.SandboxInfo()
|
||||
if sinfo != nil {
|
||||
for _, i := range sinfo.Interfaces {
|
||||
err = sb.AddInterface(i)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
err = sb.SetGateway(sinfo.Gateway)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = sb.SetGatewayIPv6(sinfo.GatewayIPv6)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
ep.containerID = containerID
|
||||
return sb.Key(), nil
|
||||
}
|
||||
|
||||
func (ep *endpoint) Leave(containerID string) error {
|
||||
if ep.containerID == "" || containerID == "" || ep.containerID != containerID {
|
||||
return InvalidContainerIDError(containerID)
|
||||
}
|
||||
|
||||
ep.network.ctrlr.sandboxRm(sandbox.GenerateKey(containerID))
|
||||
ep.containerID = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ep *endpoint) Delete() error {
|
||||
var err error
|
||||
|
||||
|
|
|
@ -51,9 +51,15 @@ func programGateway(path string, gw net.IP) error {
|
|||
}
|
||||
defer netns.Set(origns)
|
||||
|
||||
gwRoutes, err := netlink.RouteGet(gw)
|
||||
if err != nil {
|
||||
return fmt.Errorf("route for the gateway could not be found: %v", err)
|
||||
}
|
||||
|
||||
return netlink.RouteAdd(&netlink.Route{
|
||||
Scope: netlink.SCOPE_UNIVERSE,
|
||||
Gw: gw,
|
||||
Scope: netlink.SCOPE_UNIVERSE,
|
||||
LinkIndex: gwRoutes[0].LinkIndex,
|
||||
Gw: gw,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -5,12 +5,17 @@ import (
|
|||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netns"
|
||||
)
|
||||
|
||||
const prefix = "/var/lib/docker/network"
|
||||
|
||||
var once sync.Once
|
||||
|
||||
// The networkNamespace type is the linux implementation of the Sandbox
|
||||
// interface. It represents a linux network namespace, and moves an interface
|
||||
// into it when called on method AddInterface or sets the gateway etc.
|
||||
|
@ -19,6 +24,24 @@ type networkNamespace struct {
|
|||
sinfo *Info
|
||||
}
|
||||
|
||||
func creatBasePath() {
|
||||
err := os.MkdirAll(prefix, 0644)
|
||||
if err != nil && !os.IsExist(err) {
|
||||
panic("Could not create net namespace path directory")
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateKey generates a sandbox key based on the passed
|
||||
// container id.
|
||||
func GenerateKey(containerID string) string {
|
||||
maxLen := 12
|
||||
if len(containerID) < maxLen {
|
||||
maxLen = len(containerID)
|
||||
}
|
||||
|
||||
return prefix + "/" + containerID[:maxLen]
|
||||
}
|
||||
|
||||
// NewSandbox provides a new sandbox instance created in an os specific way
|
||||
// provided a key which uniquely identifies the sandbox
|
||||
func NewSandbox(key string) (Sandbox, error) {
|
||||
|
@ -63,6 +86,8 @@ func createNetworkNamespace(path string) (Sandbox, error) {
|
|||
|
||||
func createNamespaceFile(path string) (err error) {
|
||||
var f *os.File
|
||||
|
||||
once.Do(creatBasePath)
|
||||
if f, err = os.Create(path); err == nil {
|
||||
f.Close()
|
||||
}
|
||||
|
@ -130,6 +155,10 @@ func (n *networkNamespace) AddInterface(i *Interface) error {
|
|||
}
|
||||
|
||||
func (n *networkNamespace) SetGateway(gw net.IP) error {
|
||||
if len(gw) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := programGateway(n.path, gw)
|
||||
if err == nil {
|
||||
n.sinfo.Gateway = gw
|
||||
|
@ -162,5 +191,9 @@ func (n *networkNamespace) Key() string {
|
|||
func (n *networkNamespace) Destroy() error {
|
||||
// Assuming no running process is executing in this network namespace,
|
||||
// unmounting is sufficient to destroy it.
|
||||
return syscall.Unmount(n.path, syscall.MNT_DETACH)
|
||||
if err := syscall.Unmount(n.path, syscall.MNT_DETACH); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.Remove(n.path)
|
||||
}
|
||||
|
|
|
@ -54,7 +54,8 @@ func newInfo(t *testing.T) (*Info, error) {
|
|||
intf.Address = addr
|
||||
intf.Address.IP = ip4
|
||||
|
||||
ip6, addrv6, err := net.ParseCIDR("2001:DB8::ABCD/48")
|
||||
// ip6, addrv6, err := net.ParseCIDR("2001:DB8::ABCD/48")
|
||||
ip6, addrv6, err := net.ParseCIDR("fe80::2/64")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -63,7 +64,8 @@ func newInfo(t *testing.T) (*Info, error) {
|
|||
|
||||
sinfo := &Info{Interfaces: []*Interface{intf}}
|
||||
sinfo.Gateway = net.ParseIP("192.168.1.1")
|
||||
sinfo.GatewayIPv6 = net.ParseIP("2001:DB8::1")
|
||||
// sinfo.GatewayIPv6 = net.ParseIP("2001:DB8::1")
|
||||
sinfo.GatewayIPv6 = net.ParseIP("fe80::1")
|
||||
|
||||
return sinfo, nil
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue