network_routes.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. package network
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/http"
  6. "strconv"
  7. "strings"
  8. "golang.org/x/net/context"
  9. "github.com/docker/docker/api/server/httputils"
  10. "github.com/docker/docker/api/types"
  11. "github.com/docker/docker/api/types/filters"
  12. "github.com/docker/docker/api/types/network"
  13. "github.com/docker/docker/api/types/versions"
  14. "github.com/docker/libnetwork"
  15. netconst "github.com/docker/libnetwork/datastore"
  16. "github.com/docker/libnetwork/networkdb"
  17. "github.com/pkg/errors"
  18. )
  19. var (
  20. // acceptedNetworkFilters is a list of acceptable filters
  21. acceptedNetworkFilters = map[string]bool{
  22. "driver": true,
  23. "type": true,
  24. "name": true,
  25. "id": true,
  26. "label": true,
  27. "scope": true,
  28. }
  29. )
  30. func (n *networkRouter) getNetworksList(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  31. if err := httputils.ParseForm(r); err != nil {
  32. return err
  33. }
  34. filter := r.Form.Get("filters")
  35. netFilters, err := filters.FromJSON(filter)
  36. if err != nil {
  37. return err
  38. }
  39. if err := netFilters.Validate(acceptedNetworkFilters); err != nil {
  40. return err
  41. }
  42. list := []types.NetworkResource{}
  43. if nr, err := n.cluster.GetNetworks(); err == nil {
  44. list = append(list, nr...)
  45. }
  46. // Combine the network list returned by Docker daemon if it is not already
  47. // returned by the cluster manager
  48. SKIP:
  49. for _, nw := range n.backend.GetNetworks() {
  50. for _, nl := range list {
  51. if nl.ID == nw.ID() {
  52. continue SKIP
  53. }
  54. }
  55. var nr *types.NetworkResource
  56. // Versions < 1.28 fetches all the containers attached to a network
  57. // in a network list api call. It is a heavy weight operation when
  58. // run across all the networks. Starting API version 1.28, this detailed
  59. // info is available for network specific GET API (equivalent to inspect)
  60. if versions.LessThan(httputils.VersionFromContext(ctx), "1.28") {
  61. nr = n.buildDetailedNetworkResources(nw, false)
  62. } else {
  63. nr = n.buildNetworkResource(nw)
  64. }
  65. list = append(list, *nr)
  66. }
  67. list, err = filterNetworks(list, netFilters)
  68. if err != nil {
  69. return err
  70. }
  71. return httputils.WriteJSON(w, http.StatusOK, list)
  72. }
  73. type invalidRequestError struct {
  74. cause error
  75. }
  76. func (e invalidRequestError) Error() string {
  77. return e.cause.Error()
  78. }
  79. func (e invalidRequestError) InvalidParameter() {}
  80. type ambigousResultsError string
  81. func (e ambigousResultsError) Error() string {
  82. return "network " + string(e) + " is ambiguous"
  83. }
  84. func (ambigousResultsError) InvalidParameter() {}
  85. type conflictError struct {
  86. cause error
  87. }
  88. func (e conflictError) Error() string {
  89. return e.cause.Error()
  90. }
  91. func (e conflictError) Cause() error {
  92. return e.cause
  93. }
  94. func (e conflictError) Conflict() {}
  95. func nameConflict(name string) error {
  96. return conflictError{libnetwork.NetworkNameError(name)}
  97. }
  98. func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  99. if err := httputils.ParseForm(r); err != nil {
  100. return err
  101. }
  102. term := vars["id"]
  103. var (
  104. verbose bool
  105. err error
  106. )
  107. if v := r.URL.Query().Get("verbose"); v != "" {
  108. if verbose, err = strconv.ParseBool(v); err != nil {
  109. return errors.Wrapf(invalidRequestError{err}, "invalid value for verbose: %s", v)
  110. }
  111. }
  112. scope := r.URL.Query().Get("scope")
  113. isMatchingScope := func(scope, term string) bool {
  114. if term != "" {
  115. return scope == term
  116. }
  117. return true
  118. }
  119. // In case multiple networks have duplicate names, return error.
  120. // TODO (yongtang): should we wrap with version here for backward compatibility?
  121. // First find based on full ID, return immediately once one is found.
  122. // If a network appears both in swarm and local, assume it is in local first
  123. // For full name and partial ID, save the result first, and process later
  124. // in case multiple records was found based on the same term
  125. listByFullName := map[string]types.NetworkResource{}
  126. listByPartialID := map[string]types.NetworkResource{}
  127. nw := n.backend.GetNetworks()
  128. for _, network := range nw {
  129. if network.ID() == term && isMatchingScope(network.Info().Scope(), scope) {
  130. return httputils.WriteJSON(w, http.StatusOK, *n.buildDetailedNetworkResources(network, verbose))
  131. }
  132. if network.Name() == term && isMatchingScope(network.Info().Scope(), scope) {
  133. // No need to check the ID collision here as we are still in
  134. // local scope and the network ID is unique in this scope.
  135. listByFullName[network.ID()] = *n.buildDetailedNetworkResources(network, verbose)
  136. }
  137. if strings.HasPrefix(network.ID(), term) && isMatchingScope(network.Info().Scope(), scope) {
  138. // No need to check the ID collision here as we are still in
  139. // local scope and the network ID is unique in this scope.
  140. listByPartialID[network.ID()] = *n.buildDetailedNetworkResources(network, verbose)
  141. }
  142. }
  143. nwk, err := n.cluster.GetNetwork(term)
  144. if err == nil {
  145. // If the get network is passed with a specific network ID / partial network ID
  146. // or if the get network was passed with a network name and scope as swarm
  147. // return the network. Skipped using isMatchingScope because it is true if the scope
  148. // is not set which would be case if the client API v1.30
  149. if strings.HasPrefix(nwk.ID, term) || (netconst.SwarmScope == scope) {
  150. return httputils.WriteJSON(w, http.StatusOK, nwk)
  151. }
  152. }
  153. nr, _ := n.cluster.GetNetworks()
  154. for _, network := range nr {
  155. if network.ID == term && isMatchingScope(network.Scope, scope) {
  156. return httputils.WriteJSON(w, http.StatusOK, network)
  157. }
  158. if network.Name == term && isMatchingScope(network.Scope, scope) {
  159. // Check the ID collision as we are in swarm scope here, and
  160. // the map (of the listByFullName) may have already had a
  161. // network with the same ID (from local scope previously)
  162. if _, ok := listByFullName[network.ID]; !ok {
  163. listByFullName[network.ID] = network
  164. }
  165. }
  166. if strings.HasPrefix(network.ID, term) && isMatchingScope(network.Scope, scope) {
  167. // Check the ID collision as we are in swarm scope here, and
  168. // the map (of the listByPartialID) may have already had a
  169. // network with the same ID (from local scope previously)
  170. if _, ok := listByPartialID[network.ID]; !ok {
  171. listByPartialID[network.ID] = network
  172. }
  173. }
  174. }
  175. // Find based on full name, returns true only if no duplicates
  176. if len(listByFullName) == 1 {
  177. for _, v := range listByFullName {
  178. return httputils.WriteJSON(w, http.StatusOK, v)
  179. }
  180. }
  181. if len(listByFullName) > 1 {
  182. return errors.Wrapf(ambigousResultsError(term), "%d matches found based on name", len(listByFullName))
  183. }
  184. // Find based on partial ID, returns true only if no duplicates
  185. if len(listByPartialID) == 1 {
  186. for _, v := range listByPartialID {
  187. return httputils.WriteJSON(w, http.StatusOK, v)
  188. }
  189. }
  190. if len(listByPartialID) > 1 {
  191. return errors.Wrapf(ambigousResultsError(term), "%d matches found based on ID prefix", len(listByPartialID))
  192. }
  193. return libnetwork.ErrNoSuchNetwork(term)
  194. }
  195. func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  196. var create types.NetworkCreateRequest
  197. if err := httputils.ParseForm(r); err != nil {
  198. return err
  199. }
  200. if err := httputils.CheckForJSON(r); err != nil {
  201. return err
  202. }
  203. if err := json.NewDecoder(r.Body).Decode(&create); err != nil {
  204. return err
  205. }
  206. if nws, err := n.cluster.GetNetworksByName(create.Name); err == nil && len(nws) > 0 {
  207. return nameConflict(create.Name)
  208. }
  209. nw, err := n.backend.CreateNetwork(create)
  210. if err != nil {
  211. var warning string
  212. if _, ok := err.(libnetwork.NetworkNameError); ok {
  213. // check if user defined CheckDuplicate, if set true, return err
  214. // otherwise prepare a warning message
  215. if create.CheckDuplicate {
  216. return nameConflict(create.Name)
  217. }
  218. warning = libnetwork.NetworkNameError(create.Name).Error()
  219. }
  220. if _, ok := err.(libnetwork.ManagerRedirectError); !ok {
  221. return err
  222. }
  223. id, err := n.cluster.CreateNetwork(create)
  224. if err != nil {
  225. return err
  226. }
  227. nw = &types.NetworkCreateResponse{
  228. ID: id,
  229. Warning: warning,
  230. }
  231. }
  232. return httputils.WriteJSON(w, http.StatusCreated, nw)
  233. }
  234. func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  235. var connect types.NetworkConnect
  236. if err := httputils.ParseForm(r); err != nil {
  237. return err
  238. }
  239. if err := httputils.CheckForJSON(r); err != nil {
  240. return err
  241. }
  242. if err := json.NewDecoder(r.Body).Decode(&connect); err != nil {
  243. return err
  244. }
  245. // Always make sure there is no ambiguity with respect to the network ID/name
  246. nw, err := n.backend.FindUniqueNetwork(vars["id"])
  247. if err != nil {
  248. return err
  249. }
  250. return n.backend.ConnectContainerToNetwork(connect.Container, nw.ID(), connect.EndpointConfig)
  251. }
  252. func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  253. var disconnect types.NetworkDisconnect
  254. if err := httputils.ParseForm(r); err != nil {
  255. return err
  256. }
  257. if err := httputils.CheckForJSON(r); err != nil {
  258. return err
  259. }
  260. if err := json.NewDecoder(r.Body).Decode(&disconnect); err != nil {
  261. return err
  262. }
  263. return n.backend.DisconnectContainerFromNetwork(disconnect.Container, vars["id"], disconnect.Force)
  264. }
  265. func (n *networkRouter) deleteNetwork(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  266. if err := httputils.ParseForm(r); err != nil {
  267. return err
  268. }
  269. nw, err := n.findUniqueNetwork(vars["id"])
  270. if err != nil {
  271. return err
  272. }
  273. if nw.Scope == "swarm" {
  274. if err = n.cluster.RemoveNetwork(nw.ID); err != nil {
  275. return err
  276. }
  277. } else {
  278. if err := n.backend.DeleteNetwork(nw.ID); err != nil {
  279. return err
  280. }
  281. }
  282. w.WriteHeader(http.StatusNoContent)
  283. return nil
  284. }
  285. func (n *networkRouter) buildNetworkResource(nw libnetwork.Network) *types.NetworkResource {
  286. r := &types.NetworkResource{}
  287. if nw == nil {
  288. return r
  289. }
  290. info := nw.Info()
  291. r.Name = nw.Name()
  292. r.ID = nw.ID()
  293. r.Created = info.Created()
  294. r.Scope = info.Scope()
  295. r.Driver = nw.Type()
  296. r.EnableIPv6 = info.IPv6Enabled()
  297. r.Internal = info.Internal()
  298. r.Attachable = info.Attachable()
  299. r.Ingress = info.Ingress()
  300. r.Options = info.DriverOptions()
  301. r.Containers = make(map[string]types.EndpointResource)
  302. buildIpamResources(r, info)
  303. r.Labels = info.Labels()
  304. r.ConfigOnly = info.ConfigOnly()
  305. if cn := info.ConfigFrom(); cn != "" {
  306. r.ConfigFrom = network.ConfigReference{Network: cn}
  307. }
  308. peers := info.Peers()
  309. if len(peers) != 0 {
  310. r.Peers = buildPeerInfoResources(peers)
  311. }
  312. return r
  313. }
  314. func (n *networkRouter) buildDetailedNetworkResources(nw libnetwork.Network, verbose bool) *types.NetworkResource {
  315. if nw == nil {
  316. return &types.NetworkResource{}
  317. }
  318. r := n.buildNetworkResource(nw)
  319. epl := nw.Endpoints()
  320. for _, e := range epl {
  321. ei := e.Info()
  322. if ei == nil {
  323. continue
  324. }
  325. sb := ei.Sandbox()
  326. tmpID := e.ID()
  327. key := "ep-" + tmpID
  328. if sb != nil {
  329. key = sb.ContainerID()
  330. }
  331. r.Containers[key] = buildEndpointResource(tmpID, e.Name(), ei)
  332. }
  333. if !verbose {
  334. return r
  335. }
  336. services := nw.Info().Services()
  337. r.Services = make(map[string]network.ServiceInfo)
  338. for name, service := range services {
  339. tasks := []network.Task{}
  340. for _, t := range service.Tasks {
  341. tasks = append(tasks, network.Task{
  342. Name: t.Name,
  343. EndpointID: t.EndpointID,
  344. EndpointIP: t.EndpointIP,
  345. Info: t.Info,
  346. })
  347. }
  348. r.Services[name] = network.ServiceInfo{
  349. VIP: service.VIP,
  350. Ports: service.Ports,
  351. Tasks: tasks,
  352. LocalLBIndex: service.LocalLBIndex,
  353. }
  354. }
  355. return r
  356. }
  357. func buildPeerInfoResources(peers []networkdb.PeerInfo) []network.PeerInfo {
  358. peerInfo := make([]network.PeerInfo, 0, len(peers))
  359. for _, peer := range peers {
  360. peerInfo = append(peerInfo, network.PeerInfo{
  361. Name: peer.Name,
  362. IP: peer.IP,
  363. })
  364. }
  365. return peerInfo
  366. }
  367. func buildIpamResources(r *types.NetworkResource, nwInfo libnetwork.NetworkInfo) {
  368. id, opts, ipv4conf, ipv6conf := nwInfo.IpamConfig()
  369. ipv4Info, ipv6Info := nwInfo.IpamInfo()
  370. r.IPAM.Driver = id
  371. r.IPAM.Options = opts
  372. r.IPAM.Config = []network.IPAMConfig{}
  373. for _, ip4 := range ipv4conf {
  374. if ip4.PreferredPool == "" {
  375. continue
  376. }
  377. iData := network.IPAMConfig{}
  378. iData.Subnet = ip4.PreferredPool
  379. iData.IPRange = ip4.SubPool
  380. iData.Gateway = ip4.Gateway
  381. iData.AuxAddress = ip4.AuxAddresses
  382. r.IPAM.Config = append(r.IPAM.Config, iData)
  383. }
  384. if len(r.IPAM.Config) == 0 {
  385. for _, ip4Info := range ipv4Info {
  386. iData := network.IPAMConfig{}
  387. iData.Subnet = ip4Info.IPAMData.Pool.String()
  388. if ip4Info.IPAMData.Gateway != nil {
  389. iData.Gateway = ip4Info.IPAMData.Gateway.IP.String()
  390. }
  391. r.IPAM.Config = append(r.IPAM.Config, iData)
  392. }
  393. }
  394. hasIpv6Conf := false
  395. for _, ip6 := range ipv6conf {
  396. if ip6.PreferredPool == "" {
  397. continue
  398. }
  399. hasIpv6Conf = true
  400. iData := network.IPAMConfig{}
  401. iData.Subnet = ip6.PreferredPool
  402. iData.IPRange = ip6.SubPool
  403. iData.Gateway = ip6.Gateway
  404. iData.AuxAddress = ip6.AuxAddresses
  405. r.IPAM.Config = append(r.IPAM.Config, iData)
  406. }
  407. if !hasIpv6Conf {
  408. for _, ip6Info := range ipv6Info {
  409. if ip6Info.IPAMData.Pool == nil {
  410. continue
  411. }
  412. iData := network.IPAMConfig{}
  413. iData.Subnet = ip6Info.IPAMData.Pool.String()
  414. iData.Gateway = ip6Info.IPAMData.Gateway.String()
  415. r.IPAM.Config = append(r.IPAM.Config, iData)
  416. }
  417. }
  418. }
  419. func buildEndpointResource(id string, name string, info libnetwork.EndpointInfo) types.EndpointResource {
  420. er := types.EndpointResource{}
  421. er.EndpointID = id
  422. er.Name = name
  423. ei := info
  424. if ei == nil {
  425. return er
  426. }
  427. if iface := ei.Iface(); iface != nil {
  428. if mac := iface.MacAddress(); mac != nil {
  429. er.MacAddress = mac.String()
  430. }
  431. if ip := iface.Address(); ip != nil && len(ip.IP) > 0 {
  432. er.IPv4Address = ip.String()
  433. }
  434. if ipv6 := iface.AddressIPv6(); ipv6 != nil && len(ipv6.IP) > 0 {
  435. er.IPv6Address = ipv6.String()
  436. }
  437. }
  438. return er
  439. }
  440. func (n *networkRouter) postNetworksPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  441. if err := httputils.ParseForm(r); err != nil {
  442. return err
  443. }
  444. pruneFilters, err := filters.FromJSON(r.Form.Get("filters"))
  445. if err != nil {
  446. return err
  447. }
  448. pruneReport, err := n.backend.NetworksPrune(ctx, pruneFilters)
  449. if err != nil {
  450. return err
  451. }
  452. return httputils.WriteJSON(w, http.StatusOK, pruneReport)
  453. }
  454. // findUniqueNetwork will search network across different scopes (both local and swarm).
  455. // NOTE: This findUniqueNetwork is different from FindUniqueNetwork in the daemon.
  456. // In case multiple networks have duplicate names, return error.
  457. // First find based on full ID, return immediately once one is found.
  458. // If a network appears both in swarm and local, assume it is in local first
  459. // For full name and partial ID, save the result first, and process later
  460. // in case multiple records was found based on the same term
  461. // TODO (yongtang): should we wrap with version here for backward compatibility?
  462. func (n *networkRouter) findUniqueNetwork(term string) (types.NetworkResource, error) {
  463. listByFullName := map[string]types.NetworkResource{}
  464. listByPartialID := map[string]types.NetworkResource{}
  465. nw := n.backend.GetNetworks()
  466. for _, network := range nw {
  467. if network.ID() == term {
  468. return *n.buildDetailedNetworkResources(network, false), nil
  469. }
  470. if network.Name() == term && !network.Info().Ingress() {
  471. // No need to check the ID collision here as we are still in
  472. // local scope and the network ID is unique in this scope.
  473. listByFullName[network.ID()] = *n.buildDetailedNetworkResources(network, false)
  474. }
  475. if strings.HasPrefix(network.ID(), term) {
  476. // No need to check the ID collision here as we are still in
  477. // local scope and the network ID is unique in this scope.
  478. listByPartialID[network.ID()] = *n.buildDetailedNetworkResources(network, false)
  479. }
  480. }
  481. nr, _ := n.cluster.GetNetworks()
  482. for _, network := range nr {
  483. if network.ID == term {
  484. return network, nil
  485. }
  486. if network.Name == term {
  487. // Check the ID collision as we are in swarm scope here, and
  488. // the map (of the listByFullName) may have already had a
  489. // network with the same ID (from local scope previously)
  490. if _, ok := listByFullName[network.ID]; !ok {
  491. listByFullName[network.ID] = network
  492. }
  493. }
  494. if strings.HasPrefix(network.ID, term) {
  495. // Check the ID collision as we are in swarm scope here, and
  496. // the map (of the listByPartialID) may have already had a
  497. // network with the same ID (from local scope previously)
  498. if _, ok := listByPartialID[network.ID]; !ok {
  499. listByPartialID[network.ID] = network
  500. }
  501. }
  502. }
  503. // Find based on full name, returns true only if no duplicates
  504. if len(listByFullName) == 1 {
  505. for _, v := range listByFullName {
  506. return v, nil
  507. }
  508. }
  509. if len(listByFullName) > 1 {
  510. return types.NetworkResource{}, fmt.Errorf("network %s is ambiguous (%d matches found based on name)", term, len(listByFullName))
  511. }
  512. // Find based on partial ID, returns true only if no duplicates
  513. if len(listByPartialID) == 1 {
  514. for _, v := range listByPartialID {
  515. return v, nil
  516. }
  517. }
  518. if len(listByPartialID) > 1 {
  519. return types.NetworkResource{}, fmt.Errorf("network %s is ambiguous (%d matches found based on ID prefix)", term, len(listByPartialID))
  520. }
  521. return types.NetworkResource{}, libnetwork.ErrNoSuchNetwork(term)
  522. }