Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
| ... | ... |
@@ -19,54 +19,33 @@ Please refer to the [roadmap](ROADMAP.md) for more information. |
| 19 | 19 |
|
| 20 | 20 |
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. |
| 21 | 21 |
|
| 22 |
+ |
|
| 22 | 23 |
```go |
| 23 |
- // Create a new controller instance |
|
| 24 |
- controller := libnetwork.New() |
|
| 25 |
- |
|
| 26 |
- // This option is only needed for in-tree drivers. Plugins(in future) will get |
|
| 27 |
- // their options through plugin infrastructure. |
|
| 28 |
- option := options.Generic{}
|
|
| 29 |
- driver, err := controller.NewNetworkDriver("bridge", option)
|
|
| 30 |
- if err != nil {
|
|
| 31 |
- return |
|
| 32 |
- } |
|
| 33 |
- |
|
| 34 |
- netOptions := options.Generic{}
|
|
| 35 |
- // Create a network for containers to join. |
|
| 36 |
- network, err := controller.NewNetwork(driver, "network1", netOptions) |
|
| 37 |
- if err != nil {
|
|
| 38 |
- return |
|
| 39 |
- } |
|
| 40 |
- |
|
| 41 |
- // For a new container: create a sandbox instance (providing a unique key). |
|
| 42 |
- // For linux it is a filesystem path |
|
| 43 |
- networkPath := "/var/lib/docker/.../4d23e" |
|
| 44 |
- networkNamespace, err := sandbox.NewSandbox(networkPath) |
|
| 45 |
- if err != nil {
|
|
| 46 |
- return |
|
| 47 |
- } |
|
| 48 |
- |
|
| 49 |
- // For each new container: allocate IP and interfaces. The returned network |
|
| 50 |
- // settings will be used for container infos (inspect and such), as well as |
|
| 51 |
- // iptables rules for port publishing. This info is contained or accessible |
|
| 52 |
- // from the returned endpoint. |
|
| 53 |
- ep, err := network.CreateEndpoint("Endpoint1", networkNamespace.Key(), "")
|
|
| 54 |
- if err != nil {
|
|
| 55 |
- return |
|
| 56 |
- } |
|
| 57 |
- |
|
| 58 |
- // Add interfaces to the namespace. |
|
| 59 |
- sinfo := ep.SandboxInfo() |
|
| 60 |
- for _, iface := range sinfo.Interfaces {
|
|
| 61 |
- if err := networkNamespace.AddInterface(iface); err != nil {
|
|
| 62 |
- return |
|
| 63 |
- } |
|
| 64 |
- } |
|
| 65 |
- |
|
| 66 |
- // Set the gateway IP |
|
| 67 |
- if err := networkNamespace.SetGateway(sinfo.Gateway); err != nil {
|
|
| 68 |
- return |
|
| 69 |
- } |
|
| 24 |
+ // Create a new controller instance |
|
| 25 |
+ controller := libnetwork.New() |
|
| 26 |
+ |
|
| 27 |
+ // This option is only needed for in-tree drivers. Plugins(in future) will get |
|
| 28 |
+ // their options through plugin infrastructure. |
|
| 29 |
+ option := options.Generic{}
|
|
| 30 |
+ err := controller.NewNetworkDriver("bridge", option)
|
|
| 31 |
+ if err != nil {
|
|
| 32 |
+ return |
|
| 33 |
+ } |
|
| 34 |
+ |
|
| 35 |
+ netOptions := options.Generic{}
|
|
| 36 |
+ // Create a network for containers to join. |
|
| 37 |
+ network, err := controller.NewNetwork("bridge", "network1", netOptions)
|
|
| 38 |
+ if err != nil {
|
|
| 39 |
+ return |
|
| 40 |
+ } |
|
| 41 |
+ |
|
| 42 |
+ // For each new container: allocate IP and interfaces. The returned network |
|
| 43 |
+ // settings will be used for container infos (inspect and such), as well as |
|
| 44 |
+ // iptables rules for port publishing. |
|
| 45 |
+ ep, err := network.CreateEndpoint("Endpoint1", nil)
|
|
| 46 |
+ if err != nil {
|
|
| 47 |
+ return |
|
| 48 |
+ } |
|
| 70 | 49 |
``` |
| 71 | 50 |
|
| 72 | 51 |
## Future |
| ... | ... |
@@ -12,7 +12,7 @@ To suggest changes to the roadmap, including additions, please write the change |
| 12 | 12 |
#### Concepts |
| 13 | 13 |
|
| 14 | 14 |
1. Sandbox: An isolated environment. This is more or less a standard docker container. |
| 15 |
-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 |
|
| 15 |
+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 |
|
| 16 | 16 |
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. |
| 17 | 17 |
|
| 18 | 18 |
#### axioms |
| ... | ... |
@@ -3,7 +3,6 @@ package main |
| 3 | 3 |
import ( |
| 4 | 4 |
"github.com/docker/libnetwork" |
| 5 | 5 |
"github.com/docker/libnetwork/pkg/options" |
| 6 |
- "github.com/docker/libnetwork/sandbox" |
|
| 7 | 6 |
) |
| 8 | 7 |
|
| 9 | 8 |
func main() {
|
| ... | ... |
@@ -25,33 +24,20 @@ func main() {
|
| 25 | 25 |
return |
| 26 | 26 |
} |
| 27 | 27 |
|
| 28 |
- // For a new container: create a sandbox instance (providing a unique key). |
|
| 29 |
- // For linux it is a filesystem path |
|
| 30 |
- networkPath := "/var/lib/docker/.../4d23e" |
|
| 31 |
- networkNamespace, err := sandbox.NewSandbox(networkPath) |
|
| 32 |
- if err != nil {
|
|
| 33 |
- return |
|
| 34 |
- } |
|
| 35 |
- |
|
| 36 | 28 |
// For each new container: allocate IP and interfaces. The returned network |
| 37 | 29 |
// settings will be used for container infos (inspect and such), as well as |
| 38 | 30 |
// iptables rules for port publishing. This info is contained or accessible |
| 39 | 31 |
// from the returned endpoint. |
| 40 |
- ep, err := network.CreateEndpoint("Endpoint1", networkNamespace.Key(), nil)
|
|
| 32 |
+ ep, err := network.CreateEndpoint("Endpoint1", nil)
|
|
| 41 | 33 |
if err != nil {
|
| 42 | 34 |
return |
| 43 | 35 |
} |
| 44 | 36 |
|
| 45 |
- // Add interfaces to the namespace. |
|
| 46 |
- sinfo := ep.SandboxInfo() |
|
| 47 |
- for _, iface := range sinfo.Interfaces {
|
|
| 48 |
- if err := networkNamespace.AddInterface(iface); err != nil {
|
|
| 49 |
- return |
|
| 50 |
- } |
|
| 51 |
- } |
|
| 52 |
- |
|
| 53 |
- // Set the gateway IP |
|
| 54 |
- if err := networkNamespace.SetGateway(sinfo.Gateway); err != nil {
|
|
| 37 |
+ // A container can join the endpoint by providing the container ID to the join |
|
| 38 |
+ // api which returns the sandbox key which can be used to access the sandbox |
|
| 39 |
+ // created for the container during join. |
|
| 40 |
+ _, err = ep.Join("container1")
|
|
| 41 |
+ if err != nil {
|
|
| 55 | 42 |
return |
| 56 | 43 |
} |
| 57 | 44 |
} |
| ... | ... |
@@ -31,10 +31,10 @@ type Driver interface {
|
| 31 | 31 |
DeleteNetwork(nid types.UUID) error |
| 32 | 32 |
|
| 33 | 33 |
// CreateEndpoint invokes the driver method to create an endpoint |
| 34 |
- // passing the network id, endpoint id, sandbox key and driver |
|
| 34 |
+ // passing the network id, endpoint id and driver |
|
| 35 | 35 |
// specific config. The config mechanism will eventually be replaced |
| 36 | 36 |
// with labels which are yet to be introduced. |
| 37 |
- CreateEndpoint(nid, eid types.UUID, key string, config interface{}) (*sandbox.Info, error)
|
|
| 37 |
+ CreateEndpoint(nid, eid types.UUID, config interface{}) (*sandbox.Info, error)
|
|
| 38 | 38 |
|
| 39 | 39 |
// DeleteEndpoint invokes the driver method to delete an endpoint |
| 40 | 40 |
// passing the network id and endpoint id. |
| ... | ... |
@@ -57,8 +57,8 @@ type bridgeEndpoint struct {
|
| 57 | 57 |
|
| 58 | 58 |
type bridgeNetwork struct {
|
| 59 | 59 |
id types.UUID |
| 60 |
- bridge *bridgeInterface // The bridge's L3 interface |
|
| 61 |
- endpoints map[string]*bridgeEndpoint // key: sandbox id |
|
| 60 |
+ bridge *bridgeInterface // The bridge's L3 interface |
|
| 61 |
+ endpoints map[types.UUID]*bridgeEndpoint // key: endpoint id |
|
| 62 | 62 |
sync.Mutex |
| 63 | 63 |
} |
| 64 | 64 |
|
| ... | ... |
@@ -118,21 +118,19 @@ func (c *Configuration) Validate() error {
|
| 118 | 118 |
return nil |
| 119 | 119 |
} |
| 120 | 120 |
|
| 121 |
-func (n *bridgeNetwork) getEndpoint(eid types.UUID) (string, *bridgeEndpoint, error) {
|
|
| 121 |
+func (n *bridgeNetwork) getEndpoint(eid types.UUID) (*bridgeEndpoint, error) {
|
|
| 122 | 122 |
n.Lock() |
| 123 | 123 |
defer n.Unlock() |
| 124 | 124 |
|
| 125 | 125 |
if eid == "" {
|
| 126 |
- return "", nil, InvalidEndpointIDError(eid) |
|
| 126 |
+ return nil, InvalidEndpointIDError(eid) |
|
| 127 | 127 |
} |
| 128 | 128 |
|
| 129 |
- for sk, ep := range n.endpoints {
|
|
| 130 |
- if ep.id == eid {
|
|
| 131 |
- return sk, ep, nil |
|
| 132 |
- } |
|
| 129 |
+ if ep, ok := n.endpoints[eid]; ok {
|
|
| 130 |
+ return ep, nil |
|
| 133 | 131 |
} |
| 134 | 132 |
|
| 135 |
- return "", nil, nil |
|
| 133 |
+ return nil, nil |
|
| 136 | 134 |
} |
| 137 | 135 |
|
| 138 | 136 |
func (d *driver) Config(option interface{}) error {
|
| ... | ... |
@@ -184,7 +182,7 @@ func (d *driver) CreateNetwork(id types.UUID, option interface{}) error {
|
| 184 | 184 |
} |
| 185 | 185 |
|
| 186 | 186 |
// Create and set network handler in driver |
| 187 |
- d.network = &bridgeNetwork{id: id, endpoints: make(map[string]*bridgeEndpoint)}
|
|
| 187 |
+ d.network = &bridgeNetwork{id: id, endpoints: make(map[types.UUID]*bridgeEndpoint)}
|
|
| 188 | 188 |
d.Unlock() |
| 189 | 189 |
|
| 190 | 190 |
// On failure make sure to reset driver network handler to nil |
| ... | ... |
@@ -299,7 +297,7 @@ func (d *driver) DeleteNetwork(nid types.UUID) error {
|
| 299 | 299 |
return err |
| 300 | 300 |
} |
| 301 | 301 |
|
| 302 |
-func (d *driver) CreateEndpoint(nid, eid types.UUID, sboxKey string, epOptions interface{}) (*sandbox.Info, error) {
|
|
| 302 |
+func (d *driver) CreateEndpoint(nid, eid types.UUID, epOptions interface{}) (*sandbox.Info, error) {
|
|
| 303 | 303 |
var ( |
| 304 | 304 |
ipv6Addr *net.IPNet |
| 305 | 305 |
err error |
| ... | ... |
@@ -323,7 +321,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, sboxKey string, epOptions i |
| 323 | 323 |
n.Unlock() |
| 324 | 324 |
|
| 325 | 325 |
// Check if endpoint id is good and retrieve correspondent endpoint |
| 326 |
- _, ep, err := n.getEndpoint(eid) |
|
| 326 |
+ ep, err := n.getEndpoint(eid) |
|
| 327 | 327 |
if err != nil {
|
| 328 | 328 |
return nil, err |
| 329 | 329 |
} |
| ... | ... |
@@ -333,18 +331,6 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, sboxKey string, epOptions i |
| 333 | 333 |
return nil, driverapi.ErrEndpointExists |
| 334 | 334 |
} |
| 335 | 335 |
|
| 336 |
- // Check if valid sandbox key |
|
| 337 |
- if sboxKey == "" {
|
|
| 338 |
- return nil, InvalidSandboxIDError(sboxKey) |
|
| 339 |
- } |
|
| 340 |
- |
|
| 341 |
- // Check if endpoint already exists for this sandbox |
|
| 342 |
- n.Lock() |
|
| 343 |
- if _, ok := n.endpoints[sboxKey]; ok {
|
|
| 344 |
- n.Unlock() |
|
| 345 |
- return nil, driverapi.ErrEndpointExists |
|
| 346 |
- } |
|
| 347 |
- |
|
| 348 | 336 |
// Try to convert the options to endpoint configuration |
| 349 | 337 |
epConfig, err := parseEndpointOptions(epOptions) |
| 350 | 338 |
if err != nil {
|
| ... | ... |
@@ -353,15 +339,16 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, sboxKey string, epOptions i |
| 353 | 353 |
} |
| 354 | 354 |
|
| 355 | 355 |
// Create and add the endpoint |
| 356 |
+ n.Lock() |
|
| 356 | 357 |
endpoint := &bridgeEndpoint{id: eid, config: epConfig}
|
| 357 |
- n.endpoints[sboxKey] = endpoint |
|
| 358 |
+ n.endpoints[eid] = endpoint |
|
| 358 | 359 |
n.Unlock() |
| 359 | 360 |
|
| 360 | 361 |
// On failure make sure to remove the endpoint |
| 361 | 362 |
defer func() {
|
| 362 | 363 |
if err != nil {
|
| 363 | 364 |
n.Lock() |
| 364 |
- delete(n.endpoints, sboxKey) |
|
| 365 |
+ delete(n.endpoints, eid) |
|
| 365 | 366 |
n.Unlock() |
| 366 | 367 |
} |
| 367 | 368 |
}() |
| ... | ... |
@@ -408,12 +395,15 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, sboxKey string, epOptions i |
| 408 | 408 |
} |
| 409 | 409 |
}() |
| 410 | 410 |
|
| 411 |
+ mac := netutils.GenerateRandomMAC() |
|
| 411 | 412 |
// Add user specified attributes |
| 412 | 413 |
if epConfig != nil && epConfig.MacAddress != nil {
|
| 413 |
- err = netlink.LinkSetHardwareAddr(sbox, epConfig.MacAddress) |
|
| 414 |
- if err != nil {
|
|
| 415 |
- return nil, err |
|
| 416 |
- } |
|
| 414 |
+ mac = epConfig.MacAddress |
|
| 415 |
+ } |
|
| 416 |
+ |
|
| 417 |
+ err = netlink.LinkSetHardwareAddr(sbox, mac) |
|
| 418 |
+ if err != nil {
|
|
| 419 |
+ return nil, err |
|
| 417 | 420 |
} |
| 418 | 421 |
|
| 419 | 422 |
// Add bridge inherited attributes to pipe interfaces |
| ... | ... |
@@ -443,11 +433,28 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, sboxKey string, epOptions i |
| 443 | 443 |
|
| 444 | 444 |
// v6 address for the sandbox side pipe interface |
| 445 | 445 |
if config.EnableIPv6 {
|
| 446 |
- ip6, err := ipAllocator.RequestIP(n.bridge.bridgeIPv6, nil) |
|
| 446 |
+ var ip6 net.IP |
|
| 447 |
+ |
|
| 448 |
+ network := n.bridge.bridgeIPv6 |
|
| 449 |
+ if config.FixedCIDRv6 != nil {
|
|
| 450 |
+ network = config.FixedCIDRv6 |
|
| 451 |
+ } |
|
| 452 |
+ |
|
| 453 |
+ ones, _ := network.Mask.Size() |
|
| 454 |
+ if ones <= 80 {
|
|
| 455 |
+ ip6 = make(net.IP, len(network.IP)) |
|
| 456 |
+ copy(ip6, network.IP) |
|
| 457 |
+ for i, h := range mac {
|
|
| 458 |
+ ip6[i+10] = h |
|
| 459 |
+ } |
|
| 460 |
+ } |
|
| 461 |
+ |
|
| 462 |
+ ip6, err := ipAllocator.RequestIP(network, ip6) |
|
| 447 | 463 |
if err != nil {
|
| 448 | 464 |
return nil, err |
| 449 | 465 |
} |
| 450 |
- ipv6Addr = &net.IPNet{IP: ip6, Mask: n.bridge.bridgeIPv6.Mask}
|
|
| 466 |
+ |
|
| 467 |
+ ipv6Addr = &net.IPNet{IP: ip6, Mask: network.Mask}
|
|
| 451 | 468 |
} |
| 452 | 469 |
|
| 453 | 470 |
// Store the sandbox side pipe interface |
| ... | ... |
@@ -494,7 +501,7 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
|
| 494 | 494 |
n.Unlock() |
| 495 | 495 |
|
| 496 | 496 |
// Check endpoint id and if an endpoint is actually there |
| 497 |
- sboxKey, ep, err := n.getEndpoint(eid) |
|
| 497 |
+ ep, err := n.getEndpoint(eid) |
|
| 498 | 498 |
if err != nil {
|
| 499 | 499 |
return err |
| 500 | 500 |
} |
| ... | ... |
@@ -504,7 +511,7 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
|
| 504 | 504 |
|
| 505 | 505 |
// Remove it |
| 506 | 506 |
n.Lock() |
| 507 |
- delete(n.endpoints, sboxKey) |
|
| 507 |
+ delete(n.endpoints, eid) |
|
| 508 | 508 |
n.Unlock() |
| 509 | 509 |
|
| 510 | 510 |
// 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 {
|
| 512 | 512 |
defer func() {
|
| 513 | 513 |
if err != nil {
|
| 514 | 514 |
n.Lock() |
| 515 |
- if _, ok := n.endpoints[sboxKey]; !ok {
|
|
| 516 |
- n.endpoints[sboxKey] = ep |
|
| 515 |
+ if _, ok := n.endpoints[eid]; !ok {
|
|
| 516 |
+ n.endpoints[eid] = ep |
|
| 517 | 517 |
} |
| 518 | 518 |
n.Unlock() |
| 519 | 519 |
} |
| ... | ... |
@@ -76,7 +76,7 @@ func TestCreateLinkWithOptions(t *testing.T) {
|
| 76 | 76 |
mac := net.HardwareAddr([]byte{0x1e, 0x67, 0x66, 0x44, 0x55, 0x66})
|
| 77 | 77 |
epConf := &EndpointConfiguration{MacAddress: mac}
|
| 78 | 78 |
|
| 79 |
- sinfo, err := d.CreateEndpoint("net1", "ep", "s1", epConf)
|
|
| 79 |
+ sinfo, err := d.CreateEndpoint("net1", "ep", epConf)
|
|
| 80 | 80 |
if err != nil {
|
| 81 | 81 |
t.Fatalf("Failed to create a link: %s", err.Error())
|
| 82 | 82 |
} |
| ... | ... |
@@ -207,7 +207,7 @@ func TestSetDefaultGw(t *testing.T) {
|
| 207 | 207 |
t.Fatalf("Failed to create bridge: %v", err)
|
| 208 | 208 |
} |
| 209 | 209 |
|
| 210 |
- sinfo, err := d.CreateEndpoint("dummy", "ep", "sb2", nil)
|
|
| 210 |
+ sinfo, err := d.CreateEndpoint("dummy", "ep", nil)
|
|
| 211 | 211 |
if err != nil {
|
| 212 | 212 |
t.Fatalf("Failed to create endpoint: %v", err)
|
| 213 | 213 |
} |
| ... | ... |
@@ -28,7 +28,7 @@ func TestLinkCreate(t *testing.T) {
|
| 28 | 28 |
t.Fatalf("Failed to create bridge: %v", err)
|
| 29 | 29 |
} |
| 30 | 30 |
|
| 31 |
- sinfo, err := d.CreateEndpoint("dummy", "", "sb1", nil)
|
|
| 31 |
+ sinfo, err := d.CreateEndpoint("dummy", "", nil)
|
|
| 32 | 32 |
if err != nil {
|
| 33 | 33 |
if _, ok := err.(InvalidEndpointIDError); !ok {
|
| 34 | 34 |
t.Fatalf("Failed with a wrong error :%s", err.Error())
|
| ... | ... |
@@ -37,17 +37,8 @@ func TestLinkCreate(t *testing.T) {
|
| 37 | 37 |
t.Fatalf("Failed to detect invalid config")
|
| 38 | 38 |
} |
| 39 | 39 |
|
| 40 |
- sinfo, err = d.CreateEndpoint("dummy", "ep", "", nil)
|
|
| 41 |
- if err != nil {
|
|
| 42 |
- if _, ok := err.(InvalidSandboxIDError); !ok {
|
|
| 43 |
- t.Fatalf("Failed with a wrong error :%s", err.Error())
|
|
| 44 |
- } |
|
| 45 |
- } else {
|
|
| 46 |
- t.Fatalf("Failed to detect invalid config")
|
|
| 47 |
- } |
|
| 48 |
- |
|
| 49 | 40 |
// Good endpoint creation |
| 50 |
- sinfo, err = d.CreateEndpoint("dummy", "ep", "cc", nil)
|
|
| 41 |
+ sinfo, err = d.CreateEndpoint("dummy", "ep", nil)
|
|
| 51 | 42 |
if err != nil {
|
| 52 | 43 |
t.Fatalf("Failed to create a link: %s", err.Error())
|
| 53 | 44 |
} |
| ... | ... |
@@ -63,16 +54,11 @@ func TestLinkCreate(t *testing.T) {
|
| 63 | 63 |
// TODO: if we could get peer name from (sboxLnk.(*netlink.Veth)).PeerName |
| 64 | 64 |
// then we could check the MTU on hostLnk as well. |
| 65 | 65 |
|
| 66 |
- _, err = d.CreateEndpoint("dummy", "ep", "cc2", nil)
|
|
| 66 |
+ _, err = d.CreateEndpoint("dummy", "ep", nil)
|
|
| 67 | 67 |
if err == nil {
|
| 68 | 68 |
t.Fatalf("Failed to detect duplicate endpoint id on same network")
|
| 69 | 69 |
} |
| 70 | 70 |
|
| 71 |
- _, err = d.CreateEndpoint("dummy", "ep2", "cc", nil)
|
|
| 72 |
- if err == nil {
|
|
| 73 |
- t.Fatalf("Failed to detect addition of more than one endpoint to same sandbox")
|
|
| 74 |
- } |
|
| 75 |
- |
|
| 76 | 71 |
interfaces := sinfo.Interfaces |
| 77 | 72 |
if len(interfaces) != 1 {
|
| 78 | 73 |
t.Fatalf("Expected exactly one interface. Instead got %d interface(s)", len(interfaces))
|
| ... | ... |
@@ -125,12 +111,12 @@ func TestLinkCreateTwo(t *testing.T) {
|
| 125 | 125 |
t.Fatalf("Failed to create bridge: %v", err)
|
| 126 | 126 |
} |
| 127 | 127 |
|
| 128 |
- _, err = d.CreateEndpoint("dummy", "ep", "s1", nil)
|
|
| 128 |
+ _, err = d.CreateEndpoint("dummy", "ep", nil)
|
|
| 129 | 129 |
if err != nil {
|
| 130 | 130 |
t.Fatalf("Failed to create a link: %s", err.Error())
|
| 131 | 131 |
} |
| 132 | 132 |
|
| 133 |
- _, err = d.CreateEndpoint("dummy", "ep", "s1", nil)
|
|
| 133 |
+ _, err = d.CreateEndpoint("dummy", "ep", nil)
|
|
| 134 | 134 |
if err != nil {
|
| 135 | 135 |
if err != driverapi.ErrEndpointExists {
|
| 136 | 136 |
t.Fatalf("Failed with a wrong error :%s", err.Error())
|
| ... | ... |
@@ -155,7 +141,7 @@ func TestLinkCreateNoEnableIPv6(t *testing.T) {
|
| 155 | 155 |
t.Fatalf("Failed to create bridge: %v", err)
|
| 156 | 156 |
} |
| 157 | 157 |
|
| 158 |
- sinfo, err := d.CreateEndpoint("dummy", "ep", "sb2", nil)
|
|
| 158 |
+ sinfo, err := d.CreateEndpoint("dummy", "ep", nil)
|
|
| 159 | 159 |
if err != nil {
|
| 160 | 160 |
t.Fatalf("Failed to create a link: %s", err.Error())
|
| 161 | 161 |
} |
| ... | ... |
@@ -186,7 +172,7 @@ func TestLinkDelete(t *testing.T) {
|
| 186 | 186 |
t.Fatalf("Failed to create bridge: %v", err)
|
| 187 | 187 |
} |
| 188 | 188 |
|
| 189 |
- _, err = d.CreateEndpoint("dummy", "ep1", "s1", nil)
|
|
| 189 |
+ _, err = d.CreateEndpoint("dummy", "ep1", nil)
|
|
| 190 | 190 |
if err != nil {
|
| 191 | 191 |
t.Fatalf("Failed to create a link: %s", err.Error())
|
| 192 | 192 |
} |
| ... | ... |
@@ -29,8 +29,15 @@ func setupBridgeIPv6(config *Configuration, i *bridgeInterface) error {
|
| 29 | 29 |
return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err)
|
| 30 | 30 |
} |
| 31 | 31 |
|
| 32 |
- if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: bridgeIPv6}); err != nil {
|
|
| 33 |
- return &IPv6AddrAddError{ip: bridgeIPv6, err: err}
|
|
| 32 |
+ _, addrsv6, err := i.addresses() |
|
| 33 |
+ if err != nil {
|
|
| 34 |
+ return err |
|
| 35 |
+ } |
|
| 36 |
+ |
|
| 37 |
+ if len(addrsv6) == 0 {
|
|
| 38 |
+ if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: bridgeIPv6}); err != nil {
|
|
| 39 |
+ return &IPv6AddrAddError{ip: bridgeIPv6, err: err}
|
|
| 40 |
+ } |
|
| 34 | 41 |
} |
| 35 | 42 |
|
| 36 | 43 |
// Store bridge network and default gateway |
| ... | ... |
@@ -12,6 +12,9 @@ var ( |
| 12 | 12 |
// ErrInvalidNetworkDriver is returned if an invalid driver |
| 13 | 13 |
// instance is passed. |
| 14 | 14 |
ErrInvalidNetworkDriver = errors.New("invalid driver bound to network")
|
| 15 |
+ // ErrInvalidJoin is returned if a join is attempted on an endpoint |
|
| 16 |
+ // which already has a container joined. |
|
| 17 |
+ ErrInvalidJoin = errors.New("A container has already joined the endpoint")
|
|
| 15 | 18 |
) |
| 16 | 19 |
|
| 17 | 20 |
// NetworkTypeError type is returned when the network type string is not |
| ... | ... |
@@ -61,3 +64,11 @@ type UnknownEndpointError struct {
|
| 61 | 61 |
func (uee *UnknownEndpointError) Error() string {
|
| 62 | 62 |
return fmt.Sprintf("unknown endpoint %s id %s", uee.name, uee.id)
|
| 63 | 63 |
} |
| 64 |
+ |
|
| 65 |
+// InvalidContainerIDError is returned when an invalid container id is passed |
|
| 66 |
+// in Join/Leave |
|
| 67 |
+type InvalidContainerIDError string |
|
| 68 |
+ |
|
| 69 |
+func (id InvalidContainerIDError) Error() string {
|
|
| 70 |
+ return fmt.Sprintf("invalid container id %s", string(id))
|
|
| 71 |
+} |
| ... | ... |
@@ -69,7 +69,7 @@ func TestBridge(t *testing.T) {
|
| 69 | 69 |
t.Fatal(err) |
| 70 | 70 |
} |
| 71 | 71 |
|
| 72 |
- ep, err := network.CreateEndpoint("testep", "sb1", nil)
|
|
| 72 |
+ ep, err := network.CreateEndpoint("testep", nil)
|
|
| 73 | 73 |
if err != nil {
|
| 74 | 74 |
t.Fatal(err) |
| 75 | 75 |
} |
| ... | ... |
@@ -199,7 +199,7 @@ func TestDeleteNetworkWithActiveEndpoints(t *testing.T) {
|
| 199 | 199 |
t.Fatal(err) |
| 200 | 200 |
} |
| 201 | 201 |
|
| 202 |
- ep, err := network.CreateEndpoint("testep", "sb2", nil)
|
|
| 202 |
+ ep, err := network.CreateEndpoint("testep", nil)
|
|
| 203 | 203 |
if err != nil {
|
| 204 | 204 |
t.Fatal(err) |
| 205 | 205 |
} |
| ... | ... |
@@ -267,7 +267,7 @@ func TestUnknownEndpoint(t *testing.T) {
|
| 267 | 267 |
t.Fatal(err) |
| 268 | 268 |
} |
| 269 | 269 |
|
| 270 |
- ep, err := network.CreateEndpoint("testep", "sb1", nil)
|
|
| 270 |
+ ep, err := network.CreateEndpoint("testep", nil)
|
|
| 271 | 271 |
if err != nil {
|
| 272 | 272 |
t.Fatal(err) |
| 273 | 273 |
} |
| ... | ... |
@@ -308,11 +308,11 @@ func TestNetworkEndpointsWalkers(t *testing.T) {
|
| 308 | 308 |
if err != nil {
|
| 309 | 309 |
t.Fatal(err) |
| 310 | 310 |
} |
| 311 |
- ep11, err := net1.CreateEndpoint("ep11", "sbox1", nil)
|
|
| 311 |
+ ep11, err := net1.CreateEndpoint("ep11", nil)
|
|
| 312 | 312 |
if err != nil {
|
| 313 | 313 |
t.Fatal(err) |
| 314 | 314 |
} |
| 315 |
- ep12, err := net1.CreateEndpoint("ep12", "sbox2", nil)
|
|
| 315 |
+ ep12, err := net1.CreateEndpoint("ep12", nil)
|
|
| 316 | 316 |
if err != nil {
|
| 317 | 317 |
t.Fatal(err) |
| 318 | 318 |
} |
| ... | ... |
@@ -434,11 +434,11 @@ func TestNetworkQuery(t *testing.T) {
|
| 434 | 434 |
if err != nil {
|
| 435 | 435 |
t.Fatal(err) |
| 436 | 436 |
} |
| 437 |
- ep11, err := net1.CreateEndpoint("ep11", "sbox1", nil)
|
|
| 437 |
+ ep11, err := net1.CreateEndpoint("ep11", nil)
|
|
| 438 | 438 |
if err != nil {
|
| 439 | 439 |
t.Fatal(err) |
| 440 | 440 |
} |
| 441 |
- ep12, err := net1.CreateEndpoint("ep12", "sbox2", nil)
|
|
| 441 |
+ ep12, err := net1.CreateEndpoint("ep12", nil)
|
|
| 442 | 442 |
if err != nil {
|
| 443 | 443 |
t.Fatal(err) |
| 444 | 444 |
} |
| ... | ... |
@@ -469,3 +469,136 @@ func TestNetworkQuery(t *testing.T) {
|
| 469 | 469 |
} |
| 470 | 470 |
|
| 471 | 471 |
} |
| 472 |
+ |
|
| 473 |
+const containerID = "valid_container" |
|
| 474 |
+ |
|
| 475 |
+func TestEndpointJoin(t *testing.T) {
|
|
| 476 |
+ defer netutils.SetupTestNetNS(t)() |
|
| 477 |
+ |
|
| 478 |
+ n, err := createTestNetwork("bridge", "testnetwork", options.Generic{})
|
|
| 479 |
+ if err != nil {
|
|
| 480 |
+ t.Fatal(err) |
|
| 481 |
+ } |
|
| 482 |
+ |
|
| 483 |
+ ep, err := n.CreateEndpoint("ep1", nil)
|
|
| 484 |
+ if err != nil {
|
|
| 485 |
+ t.Fatal(err) |
|
| 486 |
+ } |
|
| 487 |
+ |
|
| 488 |
+ _, err = ep.Join(containerID) |
|
| 489 |
+ if err != nil {
|
|
| 490 |
+ t.Fatal(err) |
|
| 491 |
+ } |
|
| 492 |
+ |
|
| 493 |
+ err = ep.Leave(containerID) |
|
| 494 |
+ if err != nil {
|
|
| 495 |
+ t.Fatal(err) |
|
| 496 |
+ } |
|
| 497 |
+} |
|
| 498 |
+ |
|
| 499 |
+func TestEndpointJoinInvalidContainerId(t *testing.T) {
|
|
| 500 |
+ defer netutils.SetupTestNetNS(t)() |
|
| 501 |
+ |
|
| 502 |
+ n, err := createTestNetwork("bridge", "testnetwork", options.Generic{})
|
|
| 503 |
+ if err != nil {
|
|
| 504 |
+ t.Fatal(err) |
|
| 505 |
+ } |
|
| 506 |
+ |
|
| 507 |
+ ep, err := n.CreateEndpoint("ep1", nil)
|
|
| 508 |
+ if err != nil {
|
|
| 509 |
+ t.Fatal(err) |
|
| 510 |
+ } |
|
| 511 |
+ |
|
| 512 |
+ _, err = ep.Join("")
|
|
| 513 |
+ if err == nil {
|
|
| 514 |
+ t.Fatal("Expected to fail join with empty container id string")
|
|
| 515 |
+ } |
|
| 516 |
+ |
|
| 517 |
+ if _, ok := err.(libnetwork.InvalidContainerIDError); !ok {
|
|
| 518 |
+ t.Fatalf("Failed for unexpected reason: %v", err)
|
|
| 519 |
+ } |
|
| 520 |
+} |
|
| 521 |
+ |
|
| 522 |
+func TestEndpointMultipleJoins(t *testing.T) {
|
|
| 523 |
+ defer netutils.SetupTestNetNS(t)() |
|
| 524 |
+ |
|
| 525 |
+ n, err := createTestNetwork("bridge", "testnetwork", options.Generic{})
|
|
| 526 |
+ if err != nil {
|
|
| 527 |
+ t.Fatal(err) |
|
| 528 |
+ } |
|
| 529 |
+ |
|
| 530 |
+ ep, err := n.CreateEndpoint("ep1", nil)
|
|
| 531 |
+ if err != nil {
|
|
| 532 |
+ t.Fatal(err) |
|
| 533 |
+ } |
|
| 534 |
+ |
|
| 535 |
+ _, err = ep.Join(containerID) |
|
| 536 |
+ if err != nil {
|
|
| 537 |
+ t.Fatal(err) |
|
| 538 |
+ } |
|
| 539 |
+ |
|
| 540 |
+ _, err = ep.Join("container2")
|
|
| 541 |
+ if err == nil {
|
|
| 542 |
+ t.Fatal("Expected to fail multiple joins for the same endpoint")
|
|
| 543 |
+ } |
|
| 544 |
+ |
|
| 545 |
+ if err != libnetwork.ErrInvalidJoin {
|
|
| 546 |
+ t.Fatalf("Failed for unexpected reason: %v", err)
|
|
| 547 |
+ } |
|
| 548 |
+ |
|
| 549 |
+ err = ep.Leave(containerID) |
|
| 550 |
+ if err != nil {
|
|
| 551 |
+ t.Fatal(err) |
|
| 552 |
+ } |
|
| 553 |
+} |
|
| 554 |
+ |
|
| 555 |
+func TestEndpointInvalidLeave(t *testing.T) {
|
|
| 556 |
+ defer netutils.SetupTestNetNS(t)() |
|
| 557 |
+ |
|
| 558 |
+ n, err := createTestNetwork("bridge", "testnetwork", options.Generic{})
|
|
| 559 |
+ if err != nil {
|
|
| 560 |
+ t.Fatal(err) |
|
| 561 |
+ } |
|
| 562 |
+ |
|
| 563 |
+ ep, err := n.CreateEndpoint("ep1", nil)
|
|
| 564 |
+ if err != nil {
|
|
| 565 |
+ t.Fatal(err) |
|
| 566 |
+ } |
|
| 567 |
+ |
|
| 568 |
+ err = ep.Leave(containerID) |
|
| 569 |
+ if err == nil {
|
|
| 570 |
+ t.Fatal("Expected to fail leave from an endpoint which has no active join")
|
|
| 571 |
+ } |
|
| 572 |
+ |
|
| 573 |
+ if _, ok := err.(libnetwork.InvalidContainerIDError); !ok {
|
|
| 574 |
+ t.Fatalf("Failed for unexpected reason: %v", err)
|
|
| 575 |
+ } |
|
| 576 |
+ |
|
| 577 |
+ _, err = ep.Join(containerID) |
|
| 578 |
+ if err != nil {
|
|
| 579 |
+ t.Fatal(err) |
|
| 580 |
+ } |
|
| 581 |
+ |
|
| 582 |
+ err = ep.Leave("")
|
|
| 583 |
+ if err == nil {
|
|
| 584 |
+ t.Fatal("Expected to fail leave with empty container id")
|
|
| 585 |
+ } |
|
| 586 |
+ |
|
| 587 |
+ if _, ok := err.(libnetwork.InvalidContainerIDError); !ok {
|
|
| 588 |
+ t.Fatalf("Failed for unexpected reason: %v", err)
|
|
| 589 |
+ } |
|
| 590 |
+ |
|
| 591 |
+ err = ep.Leave("container2")
|
|
| 592 |
+ if err == nil {
|
|
| 593 |
+ t.Fatal("Expected to fail leave with wrong container id")
|
|
| 594 |
+ } |
|
| 595 |
+ |
|
| 596 |
+ if _, ok := err.(libnetwork.InvalidContainerIDError); !ok {
|
|
| 597 |
+ t.Fatalf("Failed for unexpected reason: %v", err)
|
|
| 598 |
+ } |
|
| 599 |
+ |
|
| 600 |
+ err = ep.Leave(containerID) |
|
| 601 |
+ if err != nil {
|
|
| 602 |
+ t.Fatal(err) |
|
| 603 |
+ } |
|
| 604 |
+} |
| ... | ... |
@@ -2,39 +2,40 @@ |
| 2 | 2 |
Package libnetwork provides the basic functionality and extension points to |
| 3 | 3 |
create network namespaces and allocate interfaces for containers to use. |
| 4 | 4 |
|
| 5 |
- // Create a new controller instance |
|
| 6 |
- controller := libnetwork.New() |
|
| 7 |
- |
|
| 8 |
- // This option is only needed for in-tree drivers. Plugins(in future) will get |
|
| 9 |
- // their options through plugin infrastructure. |
|
| 10 |
- option := options.Generic{}
|
|
| 11 |
- err := controller.NewNetworkDriver("bridge", option)
|
|
| 12 |
- if err != nil {
|
|
| 13 |
- return |
|
| 14 |
- } |
|
| 15 |
- |
|
| 16 |
- netOptions := options.Generic{}
|
|
| 17 |
- // Create a network for containers to join. |
|
| 18 |
- network, err := controller.NewNetwork("bridge", "network1", netOptions)
|
|
| 19 |
- if err != nil {
|
|
| 20 |
- return |
|
| 21 |
- } |
|
| 22 |
- |
|
| 23 |
- // For a new container: create a sandbox instance (providing a unique key). |
|
| 24 |
- // For linux it is a filesystem path |
|
| 25 |
- networkPath := "/var/lib/docker/.../4d23e" |
|
| 26 |
- networkNamespace, err := sandbox.NewSandbox(networkPath) |
|
| 27 |
- if err != nil {
|
|
| 28 |
- return |
|
| 29 |
- } |
|
| 30 |
- |
|
| 31 |
- // For each new container: allocate IP and interfaces. The returned network |
|
| 32 |
- // settings will be used for container infos (inspect and such), as well as |
|
| 33 |
- // iptables rules for port publishing. |
|
| 34 |
- ep, err := network.CreateEndpoint("Endpoint1", networkNamespace.Key(), nil)
|
|
| 35 |
- if err != nil {
|
|
| 36 |
- return |
|
| 37 |
- } |
|
| 5 |
+ // Create a new controller instance |
|
| 6 |
+ controller := libnetwork.New() |
|
| 7 |
+ |
|
| 8 |
+ // Select and configure the network driver |
|
| 9 |
+ networkType := "bridge" |
|
| 10 |
+ option := options.Generic{}
|
|
| 11 |
+ err := controller.ConfigureNetworkDriver(networkType, option) |
|
| 12 |
+ if err != nil {
|
|
| 13 |
+ return |
|
| 14 |
+ } |
|
| 15 |
+ |
|
| 16 |
+ netOptions := options.Generic{}
|
|
| 17 |
+ // Create a network for containers to join. |
|
| 18 |
+ network, err := controller.NewNetwork(networkType, "network1", netOptions) |
|
| 19 |
+ if err != nil {
|
|
| 20 |
+ return |
|
| 21 |
+ } |
|
| 22 |
+ |
|
| 23 |
+ // For each new container: allocate IP and interfaces. The returned network |
|
| 24 |
+ // settings will be used for container infos (inspect and such), as well as |
|
| 25 |
+ // iptables rules for port publishing. This info is contained or accessible |
|
| 26 |
+ // from the returned endpoint. |
|
| 27 |
+ ep, err := network.CreateEndpoint("Endpoint1", nil)
|
|
| 28 |
+ if err != nil {
|
|
| 29 |
+ return |
|
| 30 |
+ } |
|
| 31 |
+ |
|
| 32 |
+ // A container can join the endpoint by providing the container ID to the join |
|
| 33 |
+ // api which returns the sandbox key which can be used to access the sandbox |
|
| 34 |
+ // created for the container during join. |
|
| 35 |
+ _, err = ep.Join("container1")
|
|
| 36 |
+ if err != nil {
|
|
| 37 |
+ return |
|
| 38 |
+ } |
|
| 38 | 39 |
*/ |
| 39 | 40 |
package libnetwork |
| 40 | 41 |
|
| ... | ... |
@@ -85,7 +86,7 @@ type Network interface {
|
| 85 | 85 |
// Create a new endpoint to this network symbolically identified by the |
| 86 | 86 |
// specified unique name. The options parameter carry driver specific options. |
| 87 | 87 |
// Labels support will be added in the near future. |
| 88 |
- CreateEndpoint(name string, sboxKey string, options interface{}) (Endpoint, error)
|
|
| 88 |
+ CreateEndpoint(name string, options interface{}) (Endpoint, error)
|
|
| 89 | 89 |
|
| 90 | 90 |
// Delete the network. |
| 91 | 91 |
Delete() error |
| ... | ... |
@@ -118,6 +119,15 @@ type Endpoint interface {
|
| 118 | 118 |
// Network returns the name of the network to which this endpoint is attached. |
| 119 | 119 |
Network() string |
| 120 | 120 |
|
| 121 |
+ // Join creates a new sandbox for the given container ID and populates the |
|
| 122 |
+ // network resources allocated for the endpoint and joins the sandbox to |
|
| 123 |
+ // the endpoint. It returns the sandbox key to the caller |
|
| 124 |
+ Join(containerID string) (string, error) |
|
| 125 |
+ |
|
| 126 |
+ // Leave removes the sandbox associated with container ID and detaches |
|
| 127 |
+ // the network resources populated in the sandbox |
|
| 128 |
+ Leave(containerID string) error |
|
| 129 |
+ |
|
| 121 | 130 |
// SandboxInfo returns the sandbox information for this endpoint. |
| 122 | 131 |
SandboxInfo() *sandbox.Info |
| 123 | 132 |
|
| ... | ... |
@@ -144,20 +154,29 @@ type endpoint struct {
|
| 144 | 144 |
id types.UUID |
| 145 | 145 |
network *network |
| 146 | 146 |
sandboxInfo *sandbox.Info |
| 147 |
+ sandBox sandbox.Sandbox |
|
| 148 |
+ containerID string |
|
| 149 |
+} |
|
| 150 |
+ |
|
| 151 |
+type sandboxData struct {
|
|
| 152 |
+ sandbox sandbox.Sandbox |
|
| 153 |
+ refCnt int |
|
| 147 | 154 |
} |
| 148 | 155 |
|
| 149 | 156 |
type networkTable map[types.UUID]*network |
| 150 | 157 |
type endpointTable map[types.UUID]*endpoint |
| 158 |
+type sandboxTable map[string]sandboxData |
|
| 151 | 159 |
|
| 152 | 160 |
type controller struct {
|
| 153 |
- networks networkTable |
|
| 154 |
- drivers driverTable |
|
| 161 |
+ networks networkTable |
|
| 162 |
+ drivers driverTable |
|
| 163 |
+ sandboxes sandboxTable |
|
| 155 | 164 |
sync.Mutex |
| 156 | 165 |
} |
| 157 | 166 |
|
| 158 | 167 |
// New creates a new instance of network controller. |
| 159 | 168 |
func New() NetworkController {
|
| 160 |
- return &controller{networkTable{}, enumerateDrivers(), sync.Mutex{}}
|
|
| 169 |
+ return &controller{networkTable{}, enumerateDrivers(), sandboxTable{}, sync.Mutex{}}
|
|
| 161 | 170 |
} |
| 162 | 171 |
|
| 163 | 172 |
func (c *controller) ConfigureNetworkDriver(networkType string, options interface{}) error {
|
| ... | ... |
@@ -256,6 +275,51 @@ func (c *controller) NetworkByID(id string) Network {
|
| 256 | 256 |
return nil |
| 257 | 257 |
} |
| 258 | 258 |
|
| 259 |
+func (c *controller) sandboxAdd(key string) (sandbox.Sandbox, error) {
|
|
| 260 |
+ c.Lock() |
|
| 261 |
+ defer c.Unlock() |
|
| 262 |
+ |
|
| 263 |
+ sData, ok := c.sandboxes[key] |
|
| 264 |
+ if !ok {
|
|
| 265 |
+ sb, err := sandbox.NewSandbox(key) |
|
| 266 |
+ if err != nil {
|
|
| 267 |
+ return nil, err |
|
| 268 |
+ } |
|
| 269 |
+ |
|
| 270 |
+ sData = sandboxData{sandbox: sb, refCnt: 1}
|
|
| 271 |
+ c.sandboxes[key] = sData |
|
| 272 |
+ return sData.sandbox, nil |
|
| 273 |
+ } |
|
| 274 |
+ |
|
| 275 |
+ sData.refCnt++ |
|
| 276 |
+ return sData.sandbox, nil |
|
| 277 |
+} |
|
| 278 |
+ |
|
| 279 |
+func (c *controller) sandboxRm(key string) {
|
|
| 280 |
+ c.Lock() |
|
| 281 |
+ defer c.Unlock() |
|
| 282 |
+ |
|
| 283 |
+ sData := c.sandboxes[key] |
|
| 284 |
+ sData.refCnt-- |
|
| 285 |
+ |
|
| 286 |
+ if sData.refCnt == 0 {
|
|
| 287 |
+ sData.sandbox.Destroy() |
|
| 288 |
+ delete(c.sandboxes, key) |
|
| 289 |
+ } |
|
| 290 |
+} |
|
| 291 |
+ |
|
| 292 |
+func (c *controller) sandboxGet(key string) sandbox.Sandbox {
|
|
| 293 |
+ c.Lock() |
|
| 294 |
+ defer c.Unlock() |
|
| 295 |
+ |
|
| 296 |
+ sData, ok := c.sandboxes[key] |
|
| 297 |
+ if !ok {
|
|
| 298 |
+ return nil |
|
| 299 |
+ } |
|
| 300 |
+ |
|
| 301 |
+ return sData.sandbox |
|
| 302 |
+} |
|
| 303 |
+ |
|
| 259 | 304 |
func (n *network) Name() string {
|
| 260 | 305 |
return n.name |
| 261 | 306 |
} |
| ... | ... |
@@ -304,13 +368,13 @@ func (n *network) Delete() error {
|
| 304 | 304 |
return err |
| 305 | 305 |
} |
| 306 | 306 |
|
| 307 |
-func (n *network) CreateEndpoint(name string, sboxKey string, options interface{}) (Endpoint, error) {
|
|
| 307 |
+func (n *network) CreateEndpoint(name string, options interface{}) (Endpoint, error) {
|
|
| 308 | 308 |
ep := &endpoint{name: name}
|
| 309 | 309 |
ep.id = types.UUID(stringid.GenerateRandomID()) |
| 310 | 310 |
ep.network = n |
| 311 | 311 |
|
| 312 | 312 |
d := n.driver |
| 313 |
- sinfo, err := d.CreateEndpoint(n.id, ep.id, sboxKey, options) |
|
| 313 |
+ sinfo, err := d.CreateEndpoint(n.id, ep.id, options) |
|
| 314 | 314 |
if err != nil {
|
| 315 | 315 |
return nil, err |
| 316 | 316 |
} |
| ... | ... |
@@ -387,6 +451,60 @@ func (ep *endpoint) SandboxInfo() *sandbox.Info {
|
| 387 | 387 |
return ep.sandboxInfo.GetCopy() |
| 388 | 388 |
} |
| 389 | 389 |
|
| 390 |
+func (ep *endpoint) Join(containerID string) (string, error) {
|
|
| 391 |
+ if containerID == "" {
|
|
| 392 |
+ return "", InvalidContainerIDError(containerID) |
|
| 393 |
+ } |
|
| 394 |
+ |
|
| 395 |
+ if ep.containerID != "" {
|
|
| 396 |
+ return "", ErrInvalidJoin |
|
| 397 |
+ } |
|
| 398 |
+ |
|
| 399 |
+ sboxKey := sandbox.GenerateKey(containerID) |
|
| 400 |
+ sb, err := ep.network.ctrlr.sandboxAdd(sboxKey) |
|
| 401 |
+ if err != nil {
|
|
| 402 |
+ return "", err |
|
| 403 |
+ } |
|
| 404 |
+ defer func() {
|
|
| 405 |
+ if err != nil {
|
|
| 406 |
+ ep.network.ctrlr.sandboxRm(sboxKey) |
|
| 407 |
+ } |
|
| 408 |
+ }() |
|
| 409 |
+ |
|
| 410 |
+ sinfo := ep.SandboxInfo() |
|
| 411 |
+ if sinfo != nil {
|
|
| 412 |
+ for _, i := range sinfo.Interfaces {
|
|
| 413 |
+ err = sb.AddInterface(i) |
|
| 414 |
+ if err != nil {
|
|
| 415 |
+ return "", err |
|
| 416 |
+ } |
|
| 417 |
+ } |
|
| 418 |
+ |
|
| 419 |
+ err = sb.SetGateway(sinfo.Gateway) |
|
| 420 |
+ if err != nil {
|
|
| 421 |
+ return "", err |
|
| 422 |
+ } |
|
| 423 |
+ |
|
| 424 |
+ err = sb.SetGatewayIPv6(sinfo.GatewayIPv6) |
|
| 425 |
+ if err != nil {
|
|
| 426 |
+ return "", err |
|
| 427 |
+ } |
|
| 428 |
+ } |
|
| 429 |
+ |
|
| 430 |
+ ep.containerID = containerID |
|
| 431 |
+ return sb.Key(), nil |
|
| 432 |
+} |
|
| 433 |
+ |
|
| 434 |
+func (ep *endpoint) Leave(containerID string) error {
|
|
| 435 |
+ if ep.containerID == "" || containerID == "" || ep.containerID != containerID {
|
|
| 436 |
+ return InvalidContainerIDError(containerID) |
|
| 437 |
+ } |
|
| 438 |
+ |
|
| 439 |
+ ep.network.ctrlr.sandboxRm(sandbox.GenerateKey(containerID)) |
|
| 440 |
+ ep.containerID = "" |
|
| 441 |
+ return nil |
|
| 442 |
+} |
|
| 443 |
+ |
|
| 390 | 444 |
func (ep *endpoint) Delete() error {
|
| 391 | 445 |
var err error |
| 392 | 446 |
|
| ... | ... |
@@ -51,9 +51,15 @@ func programGateway(path string, gw net.IP) error {
|
| 51 | 51 |
} |
| 52 | 52 |
defer netns.Set(origns) |
| 53 | 53 |
|
| 54 |
+ gwRoutes, err := netlink.RouteGet(gw) |
|
| 55 |
+ if err != nil {
|
|
| 56 |
+ return fmt.Errorf("route for the gateway could not be found: %v", err)
|
|
| 57 |
+ } |
|
| 58 |
+ |
|
| 54 | 59 |
return netlink.RouteAdd(&netlink.Route{
|
| 55 |
- Scope: netlink.SCOPE_UNIVERSE, |
|
| 56 |
- Gw: gw, |
|
| 60 |
+ Scope: netlink.SCOPE_UNIVERSE, |
|
| 61 |
+ LinkIndex: gwRoutes[0].LinkIndex, |
|
| 62 |
+ Gw: gw, |
|
| 57 | 63 |
}) |
| 58 | 64 |
} |
| 59 | 65 |
|
| ... | ... |
@@ -5,12 +5,17 @@ import ( |
| 5 | 5 |
"net" |
| 6 | 6 |
"os" |
| 7 | 7 |
"runtime" |
| 8 |
+ "sync" |
|
| 8 | 9 |
"syscall" |
| 9 | 10 |
|
| 10 | 11 |
"github.com/vishvananda/netlink" |
| 11 | 12 |
"github.com/vishvananda/netns" |
| 12 | 13 |
) |
| 13 | 14 |
|
| 15 |
+const prefix = "/var/lib/docker/network" |
|
| 16 |
+ |
|
| 17 |
+var once sync.Once |
|
| 18 |
+ |
|
| 14 | 19 |
// The networkNamespace type is the linux implementation of the Sandbox |
| 15 | 20 |
// interface. It represents a linux network namespace, and moves an interface |
| 16 | 21 |
// into it when called on method AddInterface or sets the gateway etc. |
| ... | ... |
@@ -19,6 +24,24 @@ type networkNamespace struct {
|
| 19 | 19 |
sinfo *Info |
| 20 | 20 |
} |
| 21 | 21 |
|
| 22 |
+func creatBasePath() {
|
|
| 23 |
+ err := os.MkdirAll(prefix, 0644) |
|
| 24 |
+ if err != nil && !os.IsExist(err) {
|
|
| 25 |
+ panic("Could not create net namespace path directory")
|
|
| 26 |
+ } |
|
| 27 |
+} |
|
| 28 |
+ |
|
| 29 |
+// GenerateKey generates a sandbox key based on the passed |
|
| 30 |
+// container id. |
|
| 31 |
+func GenerateKey(containerID string) string {
|
|
| 32 |
+ maxLen := 12 |
|
| 33 |
+ if len(containerID) < maxLen {
|
|
| 34 |
+ maxLen = len(containerID) |
|
| 35 |
+ } |
|
| 36 |
+ |
|
| 37 |
+ return prefix + "/" + containerID[:maxLen] |
|
| 38 |
+} |
|
| 39 |
+ |
|
| 22 | 40 |
// NewSandbox provides a new sandbox instance created in an os specific way |
| 23 | 41 |
// provided a key which uniquely identifies the sandbox |
| 24 | 42 |
func NewSandbox(key string) (Sandbox, error) {
|
| ... | ... |
@@ -63,6 +86,8 @@ func createNetworkNamespace(path string) (Sandbox, error) {
|
| 63 | 63 |
|
| 64 | 64 |
func createNamespaceFile(path string) (err error) {
|
| 65 | 65 |
var f *os.File |
| 66 |
+ |
|
| 67 |
+ once.Do(creatBasePath) |
|
| 66 | 68 |
if f, err = os.Create(path); err == nil {
|
| 67 | 69 |
f.Close() |
| 68 | 70 |
} |
| ... | ... |
@@ -130,6 +155,10 @@ func (n *networkNamespace) AddInterface(i *Interface) error {
|
| 130 | 130 |
} |
| 131 | 131 |
|
| 132 | 132 |
func (n *networkNamespace) SetGateway(gw net.IP) error {
|
| 133 |
+ if len(gw) == 0 {
|
|
| 134 |
+ return nil |
|
| 135 |
+ } |
|
| 136 |
+ |
|
| 133 | 137 |
err := programGateway(n.path, gw) |
| 134 | 138 |
if err == nil {
|
| 135 | 139 |
n.sinfo.Gateway = gw |
| ... | ... |
@@ -162,5 +191,9 @@ func (n *networkNamespace) Key() string {
|
| 162 | 162 |
func (n *networkNamespace) Destroy() error {
|
| 163 | 163 |
// Assuming no running process is executing in this network namespace, |
| 164 | 164 |
// unmounting is sufficient to destroy it. |
| 165 |
- return syscall.Unmount(n.path, syscall.MNT_DETACH) |
|
| 165 |
+ if err := syscall.Unmount(n.path, syscall.MNT_DETACH); err != nil {
|
|
| 166 |
+ return err |
|
| 167 |
+ } |
|
| 168 |
+ |
|
| 169 |
+ return os.Remove(n.path) |
|
| 166 | 170 |
} |
| ... | ... |
@@ -54,7 +54,8 @@ func newInfo(t *testing.T) (*Info, error) {
|
| 54 | 54 |
intf.Address = addr |
| 55 | 55 |
intf.Address.IP = ip4 |
| 56 | 56 |
|
| 57 |
- ip6, addrv6, err := net.ParseCIDR("2001:DB8::ABCD/48")
|
|
| 57 |
+ // ip6, addrv6, err := net.ParseCIDR("2001:DB8::ABCD/48")
|
|
| 58 |
+ ip6, addrv6, err := net.ParseCIDR("fe80::2/64")
|
|
| 58 | 59 |
if err != nil {
|
| 59 | 60 |
return nil, err |
| 60 | 61 |
} |
| ... | ... |
@@ -63,7 +64,8 @@ func newInfo(t *testing.T) (*Info, error) {
|
| 63 | 63 |
|
| 64 | 64 |
sinfo := &Info{Interfaces: []*Interface{intf}}
|
| 65 | 65 |
sinfo.Gateway = net.ParseIP("192.168.1.1")
|
| 66 |
- sinfo.GatewayIPv6 = net.ParseIP("2001:DB8::1")
|
|
| 66 |
+ // sinfo.GatewayIPv6 = net.ParseIP("2001:DB8::1")
|
|
| 67 |
+ sinfo.GatewayIPv6 = net.ParseIP("fe80::1")
|
|
| 67 | 68 |
|
| 68 | 69 |
return sinfo, nil |
| 69 | 70 |
} |