|
@@ -1,10 +1,12 @@
|
|
|
package libnetwork
|
|
|
|
|
|
import (
|
|
|
+ "io/ioutil"
|
|
|
"os"
|
|
|
"path/filepath"
|
|
|
|
|
|
"github.com/docker/docker/pkg/etchosts"
|
|
|
+ "github.com/docker/libnetwork/driverapi"
|
|
|
"github.com/docker/libnetwork/netutils"
|
|
|
"github.com/docker/libnetwork/pkg/options"
|
|
|
"github.com/docker/libnetwork/sandbox"
|
|
@@ -49,13 +51,26 @@ type EndpointOption func(ep *endpoint)
|
|
|
// ContainerData is a set of data returned when a container joins an endpoint.
|
|
|
type ContainerData struct {
|
|
|
SandboxKey string
|
|
|
- HostsPath string
|
|
|
}
|
|
|
|
|
|
type containerConfig struct {
|
|
|
- hostName string
|
|
|
- domainName string
|
|
|
- generic map[string]interface{}
|
|
|
+ hostName string
|
|
|
+ domainName string
|
|
|
+ generic map[string]interface{}
|
|
|
+ hostsPath string
|
|
|
+ ExtraHosts []extraHost
|
|
|
+ parentUpdates []parentUpdate
|
|
|
+}
|
|
|
+
|
|
|
+type extraHost struct {
|
|
|
+ name string
|
|
|
+ IP string
|
|
|
+}
|
|
|
+
|
|
|
+type parentUpdate struct {
|
|
|
+ eid string
|
|
|
+ name string
|
|
|
+ ip string
|
|
|
}
|
|
|
|
|
|
type containerInfo struct {
|
|
@@ -70,13 +85,14 @@ type endpoint struct {
|
|
|
network *network
|
|
|
sandboxInfo *sandbox.Info
|
|
|
sandBox sandbox.Sandbox
|
|
|
+ joinInfo *driverapi.JoinInfo
|
|
|
container *containerInfo
|
|
|
exposedPorts []netutils.TransportPort
|
|
|
generic map[string]interface{}
|
|
|
context map[string]interface{}
|
|
|
}
|
|
|
|
|
|
-const prefix = "/var/lib/docker/network/files"
|
|
|
+const defaultPrefix = "/var/lib/docker/network/files"
|
|
|
|
|
|
func (ep *endpoint) ID() string {
|
|
|
return string(ep.id)
|
|
@@ -118,7 +134,7 @@ func createBasePath(dir string) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func createHostsFile(path string) error {
|
|
|
+func createFile(path string) error {
|
|
|
var f *os.File
|
|
|
|
|
|
dir, _ := filepath.Split(path)
|
|
@@ -146,7 +162,11 @@ func (ep *endpoint) Join(containerID string, options ...EndpointOption) (*Contai
|
|
|
return nil, ErrInvalidJoin
|
|
|
}
|
|
|
|
|
|
- ep.container = &containerInfo{}
|
|
|
+ ep.container = &containerInfo{
|
|
|
+ config: containerConfig{
|
|
|
+ ExtraHosts: []extraHost{},
|
|
|
+ parentUpdates: []parentUpdate{},
|
|
|
+ }}
|
|
|
defer func() {
|
|
|
if err != nil {
|
|
|
ep.container = nil
|
|
@@ -155,33 +175,49 @@ func (ep *endpoint) Join(containerID string, options ...EndpointOption) (*Contai
|
|
|
|
|
|
ep.processOptions(options...)
|
|
|
|
|
|
- ep.container.data.HostsPath = prefix + "/" + containerID + "/hosts"
|
|
|
- err = createHostsFile(ep.container.data.HostsPath)
|
|
|
+ if ep.container.config.hostsPath == "" {
|
|
|
+ ep.container.config.hostsPath = defaultPrefix + "/" + containerID + "/hosts"
|
|
|
+ }
|
|
|
+
|
|
|
+ sboxKey := sandbox.GenerateKey(containerID)
|
|
|
+
|
|
|
+ joinInfo, err := ep.network.driver.Join(ep.network.id, ep.id,
|
|
|
+ sboxKey, ep.container.config.generic)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
+ ep.joinInfo = joinInfo
|
|
|
|
|
|
err = ep.buildHostsFiles()
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
- sboxKey := sandbox.GenerateKey(containerID)
|
|
|
- sb, err := ep.network.ctrlr.sandboxAdd(sboxKey)
|
|
|
+ err = ep.updateParentHosts()
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
- defer func() {
|
|
|
- if err != nil {
|
|
|
- ep.network.ctrlr.sandboxRm(sboxKey)
|
|
|
+
|
|
|
+ create := true
|
|
|
+ if joinInfo != nil {
|
|
|
+ if joinInfo.SandboxKey != "" {
|
|
|
+ sboxKey = joinInfo.SandboxKey
|
|
|
}
|
|
|
- }()
|
|
|
|
|
|
- n := ep.network
|
|
|
- err = n.driver.Join(n.id, ep.id, sboxKey, ep.container.config.generic)
|
|
|
+ if joinInfo.NoSandboxCreate {
|
|
|
+ create = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ sb, err := ep.network.ctrlr.sandboxAdd(sboxKey, create)
|
|
|
if err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
+ defer func() {
|
|
|
+ if err != nil {
|
|
|
+ ep.network.ctrlr.sandboxRm(sboxKey)
|
|
|
+ }
|
|
|
+ }()
|
|
|
|
|
|
sinfo := ep.SandboxInfo()
|
|
|
if sinfo != nil {
|
|
@@ -254,18 +290,40 @@ func (ep *endpoint) Delete() error {
|
|
|
func (ep *endpoint) buildHostsFiles() error {
|
|
|
var extraContent []etchosts.Record
|
|
|
|
|
|
+ dir, _ := filepath.Split(ep.container.config.hostsPath)
|
|
|
+ err := createBasePath(dir)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ if ep.joinInfo != nil && ep.joinInfo.HostsPath != "" {
|
|
|
+ content, err := ioutil.ReadFile(ep.joinInfo.HostsPath)
|
|
|
+ if err != nil && !os.IsNotExist(err) {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ if err == nil {
|
|
|
+ return ioutil.WriteFile(ep.container.config.hostsPath, content, 0644)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
name := ep.container.config.hostName
|
|
|
if ep.container.config.domainName != "" {
|
|
|
name = name + "." + ep.container.config.domainName
|
|
|
}
|
|
|
|
|
|
+ for _, extraHost := range ep.container.config.ExtraHosts {
|
|
|
+ extraContent = append(extraContent,
|
|
|
+ etchosts.Record{Hosts: extraHost.name, IP: extraHost.IP})
|
|
|
+ }
|
|
|
+
|
|
|
IP := ""
|
|
|
if ep.sandboxInfo != nil && ep.sandboxInfo.Interfaces[0] != nil &&
|
|
|
ep.sandboxInfo.Interfaces[0].Address != nil {
|
|
|
IP = ep.sandboxInfo.Interfaces[0].Address.IP.String()
|
|
|
}
|
|
|
|
|
|
- return etchosts.Build(ep.container.data.HostsPath, IP, ep.container.config.hostName,
|
|
|
+ return etchosts.Build(ep.container.config.hostsPath, IP, ep.container.config.hostName,
|
|
|
ep.container.config.domainName, extraContent)
|
|
|
}
|
|
|
|
|
@@ -279,6 +337,25 @@ func EndpointOptionGeneric(generic map[string]interface{}) EndpointOption {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+func (ep *endpoint) updateParentHosts() error {
|
|
|
+ for _, update := range ep.container.config.parentUpdates {
|
|
|
+ ep.network.Lock()
|
|
|
+ pep, ok := ep.network.endpoints[types.UUID(update.eid)]
|
|
|
+ if !ok {
|
|
|
+ ep.network.Unlock()
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ ep.network.Unlock()
|
|
|
+
|
|
|
+ if err := etchosts.Update(pep.container.config.hostsPath,
|
|
|
+ update.ip, update.name); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
// JoinOptionHostname function returns an option setter for hostname option to
|
|
|
// be passed to endpoint Join method.
|
|
|
func JoinOptionHostname(name string) EndpointOption {
|
|
@@ -295,6 +372,30 @@ func JoinOptionDomainname(name string) EndpointOption {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// JoinOptionHostsPath function returns an option setter for hostspath option to
|
|
|
+// be passed to endpoint Join method.
|
|
|
+func JoinOptionHostsPath(path string) EndpointOption {
|
|
|
+ return func(ep *endpoint) {
|
|
|
+ ep.container.config.hostsPath = path
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// JoinOptionExtraHost function returns an option setter for extra /etc/hosts options
|
|
|
+// which is a name and IP as strings.
|
|
|
+func JoinOptionExtraHost(name string, IP string) EndpointOption {
|
|
|
+ return func(ep *endpoint) {
|
|
|
+ ep.container.config.ExtraHosts = append(ep.container.config.ExtraHosts, extraHost{name: name, IP: IP})
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// JoinOptionParentUpdate function returns an option setter for parent container
|
|
|
+// which needs to update the IP address for the linked container.
|
|
|
+func JoinOptionParentUpdate(eid string, name, ip string) EndpointOption {
|
|
|
+ return func(ep *endpoint) {
|
|
|
+ ep.container.config.parentUpdates = append(ep.container.config.parentUpdates, parentUpdate{eid: eid, name: name, ip: ip})
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// CreateOptionPortMapping function returns an option setter for the container exposed
|
|
|
// ports option to be passed to network.CreateEndpoint() method.
|
|
|
func CreateOptionPortMapping(portBindings []netutils.PortBinding) EndpointOption {
|