|
@@ -1,6 +1,10 @@
|
|
|
package libnetwork
|
|
|
|
|
|
import (
|
|
|
+ "os"
|
|
|
+ "path/filepath"
|
|
|
+
|
|
|
+ "github.com/docker/docker/pkg/etchosts"
|
|
|
"github.com/docker/libnetwork/sandbox"
|
|
|
"github.com/docker/libnetwork/types"
|
|
|
)
|
|
@@ -19,7 +23,7 @@ type Endpoint interface {
|
|
|
// 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)
|
|
|
+ Join(containerID string, options ...JoinOption) (*ContainerData, error)
|
|
|
|
|
|
// Leave removes the sandbox associated with container ID and detaches
|
|
|
// the network resources populated in the sandbox
|
|
@@ -32,15 +36,38 @@ type Endpoint interface {
|
|
|
Delete() error
|
|
|
}
|
|
|
|
|
|
+// ContainerData is a set of data returned when a container joins an endpoint.
|
|
|
+type ContainerData struct {
|
|
|
+ SandboxKey string
|
|
|
+ HostsPath string
|
|
|
+}
|
|
|
+
|
|
|
+// JoinOption is a option setter function type used to pass varios options to
|
|
|
+// endpoint Join method.
|
|
|
+type JoinOption func(ep Endpoint)
|
|
|
+
|
|
|
+type containerConfig struct {
|
|
|
+ Hostname string
|
|
|
+ Domainname string
|
|
|
+}
|
|
|
+
|
|
|
+type containerInfo struct {
|
|
|
+ ID string
|
|
|
+ Config containerConfig
|
|
|
+ Data ContainerData
|
|
|
+}
|
|
|
+
|
|
|
type endpoint struct {
|
|
|
name string
|
|
|
id types.UUID
|
|
|
network *network
|
|
|
sandboxInfo *sandbox.Info
|
|
|
sandBox sandbox.Sandbox
|
|
|
- containerID string
|
|
|
+ container *containerInfo
|
|
|
}
|
|
|
|
|
|
+const prefix = "/var/lib/docker/network/files"
|
|
|
+
|
|
|
func (ep *endpoint) ID() string {
|
|
|
return string(ep.id)
|
|
|
}
|
|
@@ -60,19 +87,69 @@ func (ep *endpoint) SandboxInfo() *sandbox.Info {
|
|
|
return ep.sandboxInfo.GetCopy()
|
|
|
}
|
|
|
|
|
|
-func (ep *endpoint) Join(containerID string) (string, error) {
|
|
|
+func createBasePath(dir string) error {
|
|
|
+ err := os.MkdirAll(dir, 0644)
|
|
|
+ if err != nil && !os.IsExist(err) {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func createHostsFile(path string) error {
|
|
|
+ var f *os.File
|
|
|
+
|
|
|
+ dir, _ := filepath.Split(path)
|
|
|
+ err := createBasePath(dir)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ f, err = os.Create(path)
|
|
|
+ if err == nil {
|
|
|
+ f.Close()
|
|
|
+ }
|
|
|
+
|
|
|
+ return err
|
|
|
+}
|
|
|
+
|
|
|
+func (ep *endpoint) Join(containerID string, options ...JoinOption) (*ContainerData, error) {
|
|
|
+ var err error
|
|
|
+
|
|
|
if containerID == "" {
|
|
|
- return "", InvalidContainerIDError(containerID)
|
|
|
+ return nil, InvalidContainerIDError(containerID)
|
|
|
}
|
|
|
|
|
|
- if ep.containerID != "" {
|
|
|
- return "", ErrInvalidJoin
|
|
|
+ if ep.container != nil {
|
|
|
+ return nil, ErrInvalidJoin
|
|
|
+ }
|
|
|
+
|
|
|
+ ep.container = &containerInfo{}
|
|
|
+ defer func() {
|
|
|
+ if err != nil {
|
|
|
+ ep.container = nil
|
|
|
+ }
|
|
|
+ }()
|
|
|
+
|
|
|
+ if options != nil {
|
|
|
+ ep.processOptions(options...)
|
|
|
+ }
|
|
|
+
|
|
|
+ ep.container.Data.HostsPath = prefix + "/" + containerID + "/hosts"
|
|
|
+ err = createHostsFile(ep.container.Data.HostsPath)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ err = ep.buildHostsFiles()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
}
|
|
|
|
|
|
sboxKey := sandbox.GenerateKey(containerID)
|
|
|
sb, err := ep.network.ctrlr.sandboxAdd(sboxKey)
|
|
|
if err != nil {
|
|
|
- return "", err
|
|
|
+ return nil, err
|
|
|
}
|
|
|
defer func() {
|
|
|
if err != nil {
|
|
@@ -85,32 +162,36 @@ func (ep *endpoint) Join(containerID string) (string, error) {
|
|
|
for _, i := range sinfo.Interfaces {
|
|
|
err = sb.AddInterface(i)
|
|
|
if err != nil {
|
|
|
- return "", err
|
|
|
+ return nil, err
|
|
|
}
|
|
|
}
|
|
|
|
|
|
err = sb.SetGateway(sinfo.Gateway)
|
|
|
if err != nil {
|
|
|
- return "", err
|
|
|
+ return nil, err
|
|
|
}
|
|
|
|
|
|
err = sb.SetGatewayIPv6(sinfo.GatewayIPv6)
|
|
|
if err != nil {
|
|
|
- return "", err
|
|
|
+ return nil, err
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- ep.containerID = containerID
|
|
|
- return sb.Key(), nil
|
|
|
+ ep.container.ID = containerID
|
|
|
+ ep.container.Data.SandboxKey = sb.Key()
|
|
|
+
|
|
|
+ cData := ep.container.Data
|
|
|
+ return &cData, nil
|
|
|
}
|
|
|
|
|
|
func (ep *endpoint) Leave(containerID string) error {
|
|
|
- if ep.containerID == "" || containerID == "" || ep.containerID != containerID {
|
|
|
+ if ep.container == nil || ep.container.ID == "" ||
|
|
|
+ containerID == "" || ep.container.ID != containerID {
|
|
|
return InvalidContainerIDError(containerID)
|
|
|
}
|
|
|
|
|
|
ep.network.ctrlr.sandboxRm(sandbox.GenerateKey(containerID))
|
|
|
- ep.containerID = ""
|
|
|
+ ep.container = nil
|
|
|
return nil
|
|
|
}
|
|
|
|
|
@@ -138,3 +219,45 @@ func (ep *endpoint) Delete() error {
|
|
|
err = n.driver.DeleteEndpoint(n.id, ep.id)
|
|
|
return err
|
|
|
}
|
|
|
+
|
|
|
+func (ep *endpoint) buildHostsFiles() error {
|
|
|
+ var extraContent []etchosts.Record
|
|
|
+
|
|
|
+ name := ep.container.Config.Hostname
|
|
|
+ if ep.container.Config.Domainname != "" {
|
|
|
+ name = name + "." + ep.container.Config.Domainname
|
|
|
+ }
|
|
|
+
|
|
|
+ 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,
|
|
|
+ ep.container.Config.Domainname, extraContent)
|
|
|
+}
|
|
|
+
|
|
|
+// JoinOptionHostname function returns an option setter for hostname option to
|
|
|
+// be passed to endpoint Join method.
|
|
|
+func JoinOptionHostname(name string) JoinOption {
|
|
|
+ return func(e Endpoint) {
|
|
|
+ ep := e.(*endpoint)
|
|
|
+ ep.container.Config.Hostname = name
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// JoinOptionDomainname function returns an option setter for domainname option to
|
|
|
+// be passed to endpoint Join method.
|
|
|
+func JoinOptionDomainname(name string) JoinOption {
|
|
|
+ return func(e Endpoint) {
|
|
|
+ ep := e.(*endpoint)
|
|
|
+ ep.container.Config.Domainname = name
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (ep *endpoint) processOptions(options ...JoinOption) {
|
|
|
+ for _, opt := range options {
|
|
|
+ opt(ep)
|
|
|
+ }
|
|
|
+}
|