endpoint.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. package libnetwork
  2. import (
  3. "io/ioutil"
  4. "os"
  5. "path/filepath"
  6. "github.com/docker/docker/pkg/etchosts"
  7. "github.com/docker/docker/pkg/resolvconf"
  8. "github.com/docker/libnetwork/driverapi"
  9. "github.com/docker/libnetwork/netutils"
  10. "github.com/docker/libnetwork/pkg/options"
  11. "github.com/docker/libnetwork/sandbox"
  12. "github.com/docker/libnetwork/types"
  13. )
  14. // Endpoint represents a logical connection between a network and a sandbox.
  15. type Endpoint interface {
  16. // A system generated id for this endpoint.
  17. ID() string
  18. // Name returns the name of this endpoint.
  19. Name() string
  20. // Network returns the name of the network to which this endpoint is attached.
  21. Network() string
  22. // Join creates a new sandbox for the given container ID and populates the
  23. // network resources allocated for the endpoint and joins the sandbox to
  24. // the endpoint. It returns the sandbox key to the caller
  25. Join(containerID string, options ...EndpointOption) (*ContainerData, error)
  26. // Leave removes the sandbox associated with container ID and detaches
  27. // the network resources populated in the sandbox
  28. Leave(containerID string, options ...EndpointOption) error
  29. // SandboxInfo returns the sandbox information for this endpoint.
  30. SandboxInfo() *sandbox.Info
  31. // Info returns a collection of operational data related to this endpoint retrieved from the driver
  32. Info() (map[string]interface{}, error)
  33. // Delete and detaches this endpoint from the network.
  34. Delete() error
  35. }
  36. // EndpointOption is a option setter function type used to pass varios options to Network
  37. // and Endpoint interfaces methods. The various setter functions of type EndpointOption are
  38. // provided by libnetwork, they look like <Create|Join|Leave>Option[...](...)
  39. type EndpointOption func(ep *endpoint)
  40. // ContainerData is a set of data returned when a container joins an endpoint.
  41. type ContainerData struct {
  42. SandboxKey string
  43. }
  44. // These are the container configs used to customize container /etc/hosts file.
  45. type hostsPathConfig struct {
  46. hostName string
  47. domainName string
  48. hostsPath string
  49. extraHosts []extraHost
  50. parentUpdates []parentUpdate
  51. }
  52. // These are the container configs used to customize container /etc/resolv.conf file.
  53. type resolvConfPathConfig struct {
  54. resolvConfPath string
  55. dnsList []string
  56. dnsSearchList []string
  57. }
  58. type containerConfig struct {
  59. hostsPathConfig
  60. resolvConfPathConfig
  61. generic map[string]interface{}
  62. useDefaultSandBox bool
  63. }
  64. type extraHost struct {
  65. name string
  66. IP string
  67. }
  68. type parentUpdate struct {
  69. eid string
  70. name string
  71. ip string
  72. }
  73. type containerInfo struct {
  74. id string
  75. config containerConfig
  76. data ContainerData
  77. }
  78. type endpoint struct {
  79. name string
  80. id types.UUID
  81. network *network
  82. sandboxInfo *sandbox.Info
  83. sandBox sandbox.Sandbox
  84. joinInfo *driverapi.JoinInfo
  85. container *containerInfo
  86. exposedPorts []netutils.TransportPort
  87. generic map[string]interface{}
  88. context map[string]interface{}
  89. }
  90. const defaultPrefix = "/var/lib/docker/network/files"
  91. func (ep *endpoint) ID() string {
  92. return string(ep.id)
  93. }
  94. func (ep *endpoint) Name() string {
  95. return ep.name
  96. }
  97. func (ep *endpoint) Network() string {
  98. return ep.network.name
  99. }
  100. func (ep *endpoint) SandboxInfo() *sandbox.Info {
  101. if ep.sandboxInfo == nil {
  102. return nil
  103. }
  104. return ep.sandboxInfo.GetCopy()
  105. }
  106. func (ep *endpoint) Info() (map[string]interface{}, error) {
  107. return ep.network.driver.EndpointInfo(ep.network.id, ep.id)
  108. }
  109. func (ep *endpoint) processOptions(options ...EndpointOption) {
  110. for _, opt := range options {
  111. if opt != nil {
  112. opt(ep)
  113. }
  114. }
  115. }
  116. func createBasePath(dir string) error {
  117. err := os.MkdirAll(dir, 0644)
  118. if err != nil && !os.IsExist(err) {
  119. return err
  120. }
  121. return nil
  122. }
  123. func createFile(path string) error {
  124. var f *os.File
  125. dir, _ := filepath.Split(path)
  126. err := createBasePath(dir)
  127. if err != nil {
  128. return err
  129. }
  130. f, err = os.Create(path)
  131. if err == nil {
  132. f.Close()
  133. }
  134. return err
  135. }
  136. func (ep *endpoint) Join(containerID string, options ...EndpointOption) (*ContainerData, error) {
  137. var err error
  138. if containerID == "" {
  139. return nil, InvalidContainerIDError(containerID)
  140. }
  141. if ep.container != nil {
  142. return nil, ErrInvalidJoin
  143. }
  144. ep.container = &containerInfo{
  145. config: containerConfig{
  146. hostsPathConfig: hostsPathConfig{
  147. extraHosts: []extraHost{},
  148. parentUpdates: []parentUpdate{},
  149. },
  150. }}
  151. defer func() {
  152. if err != nil {
  153. ep.container = nil
  154. }
  155. }()
  156. ep.processOptions(options...)
  157. if ep.container.config.hostsPath == "" {
  158. ep.container.config.hostsPath = defaultPrefix + "/" + containerID + "/hosts"
  159. }
  160. if ep.container.config.resolvConfPath == "" {
  161. ep.container.config.resolvConfPath = defaultPrefix + "/" + containerID + "/resolv.conf"
  162. }
  163. sboxKey := sandbox.GenerateKey(containerID)
  164. if ep.container.config.useDefaultSandBox {
  165. sboxKey = sandbox.GenerateKey("default")
  166. }
  167. joinInfo, err := ep.network.driver.Join(ep.network.id, ep.id,
  168. sboxKey, ep.container.config.generic)
  169. if err != nil {
  170. return nil, err
  171. }
  172. ep.joinInfo = joinInfo
  173. err = ep.buildHostsFiles()
  174. if err != nil {
  175. return nil, err
  176. }
  177. err = ep.updateParentHosts()
  178. if err != nil {
  179. return nil, err
  180. }
  181. err = ep.setupDNS()
  182. if err != nil {
  183. return nil, err
  184. }
  185. sb, err := ep.network.ctrlr.sandboxAdd(sboxKey,
  186. !ep.container.config.useDefaultSandBox)
  187. if err != nil {
  188. return nil, err
  189. }
  190. defer func() {
  191. if err != nil {
  192. ep.network.ctrlr.sandboxRm(sboxKey)
  193. }
  194. }()
  195. sinfo := ep.SandboxInfo()
  196. if sinfo != nil {
  197. for _, i := range sinfo.Interfaces {
  198. err = sb.AddInterface(i)
  199. if err != nil {
  200. return nil, err
  201. }
  202. }
  203. err = sb.SetGateway(sinfo.Gateway)
  204. if err != nil {
  205. return nil, err
  206. }
  207. err = sb.SetGatewayIPv6(sinfo.GatewayIPv6)
  208. if err != nil {
  209. return nil, err
  210. }
  211. }
  212. ep.container.id = containerID
  213. ep.container.data.SandboxKey = sb.Key()
  214. cData := ep.container.data
  215. return &cData, nil
  216. }
  217. func (ep *endpoint) Leave(containerID string, options ...EndpointOption) error {
  218. if ep.container == nil || ep.container.id == "" ||
  219. containerID == "" || ep.container.id != containerID {
  220. return InvalidContainerIDError(containerID)
  221. }
  222. ep.processOptions(options...)
  223. n := ep.network
  224. err := n.driver.Leave(n.id, ep.id, ep.context)
  225. ep.network.ctrlr.sandboxRm(ep.container.data.SandboxKey)
  226. ep.container = nil
  227. ep.context = nil
  228. return err
  229. }
  230. func (ep *endpoint) Delete() error {
  231. var err error
  232. n := ep.network
  233. n.Lock()
  234. _, ok := n.endpoints[ep.id]
  235. if !ok {
  236. n.Unlock()
  237. return &UnknownEndpointError{name: ep.name, id: string(ep.id)}
  238. }
  239. delete(n.endpoints, ep.id)
  240. n.Unlock()
  241. defer func() {
  242. if err != nil {
  243. n.Lock()
  244. n.endpoints[ep.id] = ep
  245. n.Unlock()
  246. }
  247. }()
  248. err = n.driver.DeleteEndpoint(n.id, ep.id)
  249. return err
  250. }
  251. func (ep *endpoint) buildHostsFiles() error {
  252. var extraContent []etchosts.Record
  253. dir, _ := filepath.Split(ep.container.config.hostsPath)
  254. err := createBasePath(dir)
  255. if err != nil {
  256. return err
  257. }
  258. if ep.joinInfo != nil && ep.joinInfo.HostsPath != "" {
  259. content, err := ioutil.ReadFile(ep.joinInfo.HostsPath)
  260. if err != nil && !os.IsNotExist(err) {
  261. return err
  262. }
  263. if err == nil {
  264. return ioutil.WriteFile(ep.container.config.hostsPath, content, 0644)
  265. }
  266. }
  267. name := ep.container.config.hostName
  268. if ep.container.config.domainName != "" {
  269. name = name + "." + ep.container.config.domainName
  270. }
  271. for _, extraHost := range ep.container.config.extraHosts {
  272. extraContent = append(extraContent,
  273. etchosts.Record{Hosts: extraHost.name, IP: extraHost.IP})
  274. }
  275. IP := ""
  276. if ep.sandboxInfo != nil && ep.sandboxInfo.Interfaces[0] != nil &&
  277. ep.sandboxInfo.Interfaces[0].Address != nil {
  278. IP = ep.sandboxInfo.Interfaces[0].Address.IP.String()
  279. }
  280. return etchosts.Build(ep.container.config.hostsPath, IP, ep.container.config.hostName,
  281. ep.container.config.domainName, extraContent)
  282. }
  283. func (ep *endpoint) updateParentHosts() error {
  284. for _, update := range ep.container.config.parentUpdates {
  285. ep.network.Lock()
  286. pep, ok := ep.network.endpoints[types.UUID(update.eid)]
  287. if !ok {
  288. ep.network.Unlock()
  289. continue
  290. }
  291. ep.network.Unlock()
  292. if err := etchosts.Update(pep.container.config.hostsPath,
  293. update.ip, update.name); err != nil {
  294. return err
  295. }
  296. }
  297. return nil
  298. }
  299. func (ep *endpoint) setupDNS() error {
  300. dir, _ := filepath.Split(ep.container.config.resolvConfPath)
  301. err := createBasePath(dir)
  302. if err != nil {
  303. return err
  304. }
  305. resolvConf, err := resolvconf.Get()
  306. if err != nil {
  307. return err
  308. }
  309. if len(ep.container.config.dnsList) > 0 ||
  310. len(ep.container.config.dnsSearchList) > 0 {
  311. var (
  312. dnsList = resolvconf.GetNameservers(resolvConf)
  313. dnsSearchList = resolvconf.GetSearchDomains(resolvConf)
  314. )
  315. if len(ep.container.config.dnsList) > 0 {
  316. dnsList = ep.container.config.dnsList
  317. }
  318. if len(ep.container.config.dnsSearchList) > 0 {
  319. dnsSearchList = ep.container.config.dnsSearchList
  320. }
  321. return resolvconf.Build(ep.container.config.resolvConfPath, dnsList, dnsSearchList)
  322. }
  323. // replace any localhost/127.* but always discard IPv6 entries for now.
  324. resolvConf, _ = resolvconf.FilterResolvDns(resolvConf, false)
  325. return ioutil.WriteFile(ep.container.config.resolvConfPath, resolvConf, 0644)
  326. }
  327. // EndpointOptionGeneric function returns an option setter for a Generic option defined
  328. // in a Dictionary of Key-Value pair
  329. func EndpointOptionGeneric(generic map[string]interface{}) EndpointOption {
  330. return func(ep *endpoint) {
  331. for k, v := range generic {
  332. ep.generic[k] = v
  333. }
  334. }
  335. }
  336. // JoinOptionHostname function returns an option setter for hostname option to
  337. // be passed to endpoint Join method.
  338. func JoinOptionHostname(name string) EndpointOption {
  339. return func(ep *endpoint) {
  340. ep.container.config.hostName = name
  341. }
  342. }
  343. // JoinOptionDomainname function returns an option setter for domainname option to
  344. // be passed to endpoint Join method.
  345. func JoinOptionDomainname(name string) EndpointOption {
  346. return func(ep *endpoint) {
  347. ep.container.config.domainName = name
  348. }
  349. }
  350. // JoinOptionHostsPath function returns an option setter for hostspath option to
  351. // be passed to endpoint Join method.
  352. func JoinOptionHostsPath(path string) EndpointOption {
  353. return func(ep *endpoint) {
  354. ep.container.config.hostsPath = path
  355. }
  356. }
  357. // JoinOptionExtraHost function returns an option setter for extra /etc/hosts options
  358. // which is a name and IP as strings.
  359. func JoinOptionExtraHost(name string, IP string) EndpointOption {
  360. return func(ep *endpoint) {
  361. ep.container.config.extraHosts = append(ep.container.config.extraHosts, extraHost{name: name, IP: IP})
  362. }
  363. }
  364. // JoinOptionParentUpdate function returns an option setter for parent container
  365. // which needs to update the IP address for the linked container.
  366. func JoinOptionParentUpdate(eid string, name, ip string) EndpointOption {
  367. return func(ep *endpoint) {
  368. ep.container.config.parentUpdates = append(ep.container.config.parentUpdates, parentUpdate{eid: eid, name: name, ip: ip})
  369. }
  370. }
  371. // JoinOptionResolvConfPath function returns an option setter for resolvconfpath option to
  372. // be passed to endpoint Join method.
  373. func JoinOptionResolvConfPath(path string) EndpointOption {
  374. return func(ep *endpoint) {
  375. ep.container.config.resolvConfPath = path
  376. }
  377. }
  378. // JoinOptionDNS function returns an option setter for dns entry option to
  379. // be passed to endpoint Join method.
  380. func JoinOptionDNS(dns string) EndpointOption {
  381. return func(ep *endpoint) {
  382. ep.container.config.dnsList = append(ep.container.config.dnsList, dns)
  383. }
  384. }
  385. // JoinOptionDNSSearch function returns an option setter for dns search entry option to
  386. // be passed to endpoint Join method.
  387. func JoinOptionDNSSearch(search string) EndpointOption {
  388. return func(ep *endpoint) {
  389. ep.container.config.dnsSearchList = append(ep.container.config.dnsSearchList, search)
  390. }
  391. }
  392. // JoinOptionUseDefaultSandbox function returns an option setter for using default sandbox to
  393. // be passed to endpoint Join method.
  394. func JoinOptionUseDefaultSandbox() EndpointOption {
  395. return func(ep *endpoint) {
  396. ep.container.config.useDefaultSandBox = true
  397. }
  398. }
  399. // CreateOptionExposedPorts function returns an option setter for the container exposed
  400. // ports option to be passed to network.CreateEndpoint() method.
  401. func CreateOptionExposedPorts(exposedPorts []netutils.TransportPort) EndpointOption {
  402. return func(ep *endpoint) {
  403. // Defensive copy
  404. eps := make([]netutils.TransportPort, len(exposedPorts))
  405. copy(eps, exposedPorts)
  406. // Store endpoint label and in generic because driver needs it
  407. ep.exposedPorts = eps
  408. ep.generic[options.ExposedPorts] = eps
  409. }
  410. }
  411. // CreateOptionPortMapping function returns an option setter for the mapping
  412. // ports option to be passed to network.CreateEndpoint() method.
  413. func CreateOptionPortMapping(portBindings []netutils.PortBinding) EndpointOption {
  414. return func(ep *endpoint) {
  415. // Store a copy of the bindings as generic data to pass to the driver
  416. pbs := make([]netutils.PortBinding, len(portBindings))
  417. copy(pbs, portBindings)
  418. ep.generic[options.PortMap] = pbs
  419. }
  420. }
  421. // JoinOptionGeneric function returns an option setter for Generic configuration
  422. // that is not managed by libNetwork but can be used by the Drivers during the call to
  423. // endpoint join method. Container Labels are a good example.
  424. func JoinOptionGeneric(generic map[string]interface{}) EndpointOption {
  425. return func(ep *endpoint) {
  426. ep.container.config.generic = generic
  427. }
  428. }
  429. // LeaveOptionGeneric function returns an option setter for Generic configuration
  430. // that is not managed by libNetwork but can be used by the Drivers during the call to
  431. // endpoint leave method. Container Labels are a good example.
  432. func LeaveOptionGeneric(context map[string]interface{}) EndpointOption {
  433. return func(ep *endpoint) {
  434. ep.context = context
  435. }
  436. }