api.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. package api
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io/ioutil"
  6. "net/http"
  7. "github.com/docker/libnetwork"
  8. "github.com/docker/libnetwork/netutils"
  9. "github.com/gorilla/mux"
  10. )
  11. var (
  12. successResponse = responseStatus{Status: "Success", StatusCode: http.StatusOK}
  13. createdResponse = responseStatus{Status: "Created", StatusCode: http.StatusCreated}
  14. mismatchResponse = responseStatus{Status: "Body/URI parameter mismatch", StatusCode: http.StatusBadRequest}
  15. )
  16. const (
  17. urlNwName = "name"
  18. urlNwID = "id"
  19. urlEpName = "endpoint-name"
  20. urlEpID = "endpoint-id"
  21. urlCnID = "container-id"
  22. )
  23. // NewHTTPHandler creates and initialize the HTTP handler to serve the requests for libnetwork
  24. func NewHTTPHandler(c libnetwork.NetworkController) func(w http.ResponseWriter, req *http.Request) {
  25. h := &httpHandler{c: c}
  26. h.initRouter()
  27. return h.handleRequest
  28. }
  29. type responseStatus struct {
  30. Status string
  31. StatusCode int
  32. }
  33. func (r *responseStatus) isOK() bool {
  34. return r.StatusCode == http.StatusOK || r.StatusCode == http.StatusCreated
  35. }
  36. type processor func(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus)
  37. type httpHandler struct {
  38. c libnetwork.NetworkController
  39. r *mux.Router
  40. }
  41. func (h *httpHandler) handleRequest(w http.ResponseWriter, req *http.Request) {
  42. // Make sure the service is there
  43. if h.c == nil {
  44. http.Error(w, "NetworkController is not available", http.StatusServiceUnavailable)
  45. return
  46. }
  47. // Get handler from router and execute it
  48. h.r.ServeHTTP(w, req)
  49. }
  50. func (h *httpHandler) initRouter() {
  51. m := map[string]map[string]processor{
  52. "GET": {
  53. "/networks": procGetNetworks,
  54. "/networks/name/{" + urlNwName + ":.*}": procGetNetwork,
  55. "/networks/id/{" + urlNwID + ":.*}": procGetNetwork,
  56. "/networks/name/{" + urlNwName + ":.*}/endpoints/": procGetEndpoints,
  57. "/networks/id/{" + urlNwID + ":.*}/endpoints/": procGetEndpoints,
  58. "/networks/name/{" + urlNwName + ":.*}/endpoints/name/{" + urlEpName + ":.*}": procGetEndpoint,
  59. "/networks/id/{" + urlNwID + ":.*}/endpoints/name/{" + urlEpName + ":.*}": procGetEndpoint,
  60. "/networks/name/{" + urlNwName + ":.*}/endpoints/id/{" + urlEpID + ":.*}": procGetEndpoint,
  61. "/networks/id/{" + urlNwID + ":.*}/endpoints/id/{" + urlEpID + ":.*}": procGetEndpoint,
  62. },
  63. "POST": {
  64. "/networks/name/{" + urlNwName + ":.*}": procCreateNetwork,
  65. "/networks/name/{" + urlNwName + ":.*}/endpoint/name/{" + urlEpName + ":.*}": procCreateEndpoint,
  66. "/networks/name/{" + urlNwName + ":.*}/endpoint/name/{" + urlEpName + ":.*}/container/{" + urlCnID + ":.*}": procJoinEndpoint,
  67. },
  68. "DELETE": {
  69. "/networks/name/{" + urlNwName + ":.*}": procDeleteNetwork,
  70. "/networks/id/{" + urlNwID + ":.*}": procDeleteNetwork,
  71. "/networks/name/{" + urlNwName + ":.*}/endpoints/name/{" + urlEpName + ":.*}": procDeleteEndpoint,
  72. "/networks/name/{" + urlNwName + ":.*}/endpoints/id/{" + urlEpID + ":.*}": procDeleteEndpoint,
  73. "/networks/id/{" + urlNwID + ":.*}/endpoints/name/{" + urlEpName + ":.*}": procDeleteEndpoint,
  74. "/networks/id/{" + urlNwID + ":.*}/endpoints/id/{" + urlEpID + ":.*}": procDeleteEndpoint,
  75. "/networks/name/{" + urlNwName + ":.*}/endpoint/name/{" + urlEpName + ":.*}/container/{" + urlCnID + ":.*}": procLeaveEndpoint,
  76. "/networks/name/{" + urlNwName + ":.*}/endpoint/id/{" + urlEpID + ":.*}/container/{" + urlCnID + ":.*}": procLeaveEndpoint,
  77. "/networks/id/{" + urlNwID + ":.*}/endpoint/name/{" + urlEpName + ":.*}/container/{" + urlCnID + ":.*}": procLeaveEndpoint,
  78. "/networks/id/{" + urlNwID + ":.*}/endpoint/id/{" + urlEpID + ":.*}/container/{" + urlCnID + ":.*}": procLeaveEndpoint,
  79. },
  80. }
  81. h.r = mux.NewRouter()
  82. for method, routes := range m {
  83. for route, fct := range routes {
  84. f := makeHandler(h.c, fct)
  85. h.r.Path(route).Methods(method).HandlerFunc(f)
  86. }
  87. }
  88. }
  89. func makeHandler(ctrl libnetwork.NetworkController, fct processor) http.HandlerFunc {
  90. return func(w http.ResponseWriter, req *http.Request) {
  91. var (
  92. body []byte
  93. err error
  94. )
  95. if req.Body != nil {
  96. body, err = ioutil.ReadAll(req.Body)
  97. if err != nil {
  98. http.Error(w, "Invalid body: "+err.Error(), http.StatusBadRequest)
  99. return
  100. }
  101. }
  102. res, rsp := fct(ctrl, mux.Vars(req), body)
  103. if !rsp.isOK() {
  104. http.Error(w, rsp.Status, rsp.StatusCode)
  105. return
  106. }
  107. if res != nil {
  108. writeJSON(w, rsp.StatusCode, res)
  109. }
  110. }
  111. }
  112. /***********
  113. Resources
  114. ************/
  115. // networkResource is the body of the "get network" http response message
  116. type networkResource struct {
  117. Name string
  118. ID string
  119. Type string
  120. Endpoints []*endpointResource
  121. }
  122. // endpointResource is the body of the "get endpoint" http response message
  123. type endpointResource struct {
  124. Name string
  125. ID string
  126. Network string
  127. }
  128. func buildNetworkResource(nw libnetwork.Network) *networkResource {
  129. r := &networkResource{}
  130. if nw != nil {
  131. r.Name = nw.Name()
  132. r.ID = nw.ID()
  133. r.Type = nw.Type()
  134. epl := nw.Endpoints()
  135. r.Endpoints = make([]*endpointResource, 0, len(epl))
  136. for _, e := range epl {
  137. epr := buildEndpointResource(e)
  138. r.Endpoints = append(r.Endpoints, epr)
  139. }
  140. }
  141. return r
  142. }
  143. func buildEndpointResource(ep libnetwork.Endpoint) *endpointResource {
  144. r := &endpointResource{}
  145. if ep != nil {
  146. r.Name = ep.Name()
  147. r.ID = ep.ID()
  148. r.Network = ep.Network()
  149. }
  150. return r
  151. }
  152. /***********
  153. Body types
  154. ************/
  155. // networkCreate is the expected body of the "create network" http request message
  156. type networkCreate struct {
  157. Name string
  158. NetworkType string
  159. Options map[string]interface{}
  160. }
  161. // endpointCreate represents the body of the "create endpoint" http request message
  162. type endpointCreate struct {
  163. Name string
  164. NetworkID string
  165. ExposedPorts []netutils.TransportPort
  166. PortMapping []netutils.PortBinding
  167. }
  168. // endpointJoin represents the expected body of the "join endpoint" or "leave endpoint" http request messages
  169. type endpointJoin struct {
  170. ContainerID string
  171. HostName string
  172. DomainName string
  173. HostsPath string
  174. ResolvConfPath string
  175. DNS []string
  176. ExtraHosts []endpointExtraHost
  177. ParentUpdates []endpointParentUpdate
  178. UseDefaultSandbox bool
  179. }
  180. // EndpointExtraHost represents the extra host object
  181. type endpointExtraHost struct {
  182. Name string
  183. Address string
  184. }
  185. // EndpointParentUpdate is the object carrying the information about the
  186. // endpoint parent that needs to be updated
  187. type endpointParentUpdate struct {
  188. EndpointID string
  189. Name string
  190. Address string
  191. }
  192. func (ej *endpointJoin) parseOptions() []libnetwork.EndpointOption {
  193. var setFctList []libnetwork.EndpointOption
  194. if ej.HostName != "" {
  195. setFctList = append(setFctList, libnetwork.JoinOptionHostname(ej.HostName))
  196. }
  197. if ej.DomainName != "" {
  198. setFctList = append(setFctList, libnetwork.JoinOptionDomainname(ej.DomainName))
  199. }
  200. if ej.HostsPath != "" {
  201. setFctList = append(setFctList, libnetwork.JoinOptionHostsPath(ej.HostsPath))
  202. }
  203. if ej.ResolvConfPath != "" {
  204. setFctList = append(setFctList, libnetwork.JoinOptionResolvConfPath(ej.ResolvConfPath))
  205. }
  206. if ej.UseDefaultSandbox {
  207. setFctList = append(setFctList, libnetwork.JoinOptionUseDefaultSandbox())
  208. }
  209. if ej.DNS != nil {
  210. for _, d := range ej.DNS {
  211. setFctList = append(setFctList, libnetwork.JoinOptionDNS(d))
  212. }
  213. }
  214. if ej.ExtraHosts != nil {
  215. for _, e := range ej.ExtraHosts {
  216. setFctList = append(setFctList, libnetwork.JoinOptionExtraHost(e.Name, e.Address))
  217. }
  218. }
  219. if ej.ParentUpdates != nil {
  220. for _, p := range ej.ParentUpdates {
  221. setFctList = append(setFctList, libnetwork.JoinOptionParentUpdate(p.EndpointID, p.Name, p.Address))
  222. }
  223. }
  224. return setFctList
  225. }
  226. /******************
  227. Process functions
  228. *******************/
  229. /***************************
  230. NetworkController interface
  231. ****************************/
  232. func procCreateNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
  233. var create networkCreate
  234. err := json.Unmarshal(body, &create)
  235. if err != nil {
  236. return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
  237. }
  238. name := vars[urlNwName]
  239. if name != create.Name {
  240. return "", &mismatchResponse
  241. }
  242. nw, err := c.NewNetwork(create.NetworkType, name, nil)
  243. if err != nil {
  244. return "", convertNetworkError(err)
  245. }
  246. return nw.ID(), &createdResponse
  247. }
  248. func procGetNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
  249. t, by := detectNetworkTarget(vars)
  250. nw, errRsp := findNetwork(c, t, by)
  251. if !errRsp.isOK() {
  252. return nil, errRsp
  253. }
  254. return buildNetworkResource(nw), &successResponse
  255. }
  256. func procGetNetworks(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
  257. var list []*networkResource
  258. for _, nw := range c.Networks() {
  259. nwr := buildNetworkResource(nw)
  260. list = append(list, nwr)
  261. }
  262. return list, &successResponse
  263. }
  264. /******************
  265. Network interface
  266. *******************/
  267. func procCreateEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
  268. var ec endpointCreate
  269. err := json.Unmarshal(body, &ec)
  270. if err != nil {
  271. return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
  272. }
  273. epn := vars[urlEpName]
  274. if ec.Name != epn {
  275. return "", &mismatchResponse
  276. }
  277. nwT, nwBy := detectNetworkTarget(vars)
  278. n, errRsp := findNetwork(c, nwT, nwBy)
  279. if !errRsp.isOK() {
  280. return "", errRsp
  281. }
  282. if ec.NetworkID != n.ID() {
  283. return "", &mismatchResponse
  284. }
  285. var setFctList []libnetwork.EndpointOption
  286. if ec.ExposedPorts != nil {
  287. setFctList = append(setFctList, libnetwork.CreateOptionExposedPorts(ec.ExposedPorts))
  288. }
  289. if ec.PortMapping != nil {
  290. setFctList = append(setFctList, libnetwork.CreateOptionPortMapping(ec.PortMapping))
  291. }
  292. ep, err := n.CreateEndpoint(epn, setFctList...)
  293. if err != nil {
  294. return "", convertNetworkError(err)
  295. }
  296. return ep.ID(), &createdResponse
  297. }
  298. func procGetEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
  299. nwT, nwBy := detectNetworkTarget(vars)
  300. epT, epBy := detectEndpointTarget(vars)
  301. ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
  302. if !errRsp.isOK() {
  303. return nil, errRsp
  304. }
  305. return buildEndpointResource(ep), &successResponse
  306. }
  307. func procGetEndpoints(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
  308. target, by := detectNetworkTarget(vars)
  309. nw, errRsp := findNetwork(c, target, by)
  310. if !errRsp.isOK() {
  311. return nil, errRsp
  312. }
  313. var list []*endpointResource
  314. for _, ep := range nw.Endpoints() {
  315. epr := buildEndpointResource(ep)
  316. list = append(list, epr)
  317. }
  318. return list, &successResponse
  319. }
  320. func procDeleteNetwork(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
  321. target, by := detectNetworkTarget(vars)
  322. nw, errRsp := findNetwork(c, target, by)
  323. if !errRsp.isOK() {
  324. return nil, errRsp
  325. }
  326. err := nw.Delete()
  327. if err != nil {
  328. return nil, convertNetworkError(err)
  329. }
  330. return nil, &successResponse
  331. }
  332. /******************
  333. Endpoint interface
  334. *******************/
  335. func procJoinEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
  336. var ej endpointJoin
  337. err := json.Unmarshal(body, &ej)
  338. if err != nil {
  339. return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
  340. }
  341. cid := vars[urlCnID]
  342. if ej.ContainerID != cid {
  343. return "", &mismatchResponse
  344. }
  345. nwT, nwBy := detectNetworkTarget(vars)
  346. epT, epBy := detectEndpointTarget(vars)
  347. ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
  348. if !errRsp.isOK() {
  349. return nil, errRsp
  350. }
  351. cd, err := ep.Join(ej.ContainerID, ej.parseOptions()...)
  352. if err != nil {
  353. return nil, convertNetworkError(err)
  354. }
  355. return cd, &successResponse
  356. }
  357. func procLeaveEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
  358. nwT, nwBy := detectNetworkTarget(vars)
  359. epT, epBy := detectEndpointTarget(vars)
  360. ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
  361. if !errRsp.isOK() {
  362. return nil, errRsp
  363. }
  364. err := ep.Leave(vars[urlCnID])
  365. if err != nil {
  366. return nil, convertNetworkError(err)
  367. }
  368. return nil, &successResponse
  369. }
  370. func procDeleteEndpoint(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
  371. nwT, nwBy := detectNetworkTarget(vars)
  372. epT, epBy := detectEndpointTarget(vars)
  373. ep, errRsp := findEndpoint(c, nwT, epT, nwBy, epBy)
  374. if !errRsp.isOK() {
  375. return nil, errRsp
  376. }
  377. err := ep.Delete()
  378. if err != nil {
  379. return nil, convertNetworkError(err)
  380. }
  381. return nil, &successResponse
  382. }
  383. /***********
  384. Utilities
  385. ************/
  386. const (
  387. byID = iota
  388. byName
  389. )
  390. func detectNetworkTarget(vars map[string]string) (string, int) {
  391. if target, ok := vars[urlNwName]; ok {
  392. return target, byName
  393. }
  394. if target, ok := vars[urlNwID]; ok {
  395. return target, byID
  396. }
  397. // vars are populated from the URL, following cannot happen
  398. panic("Missing URL variable parameter for network")
  399. }
  400. func detectEndpointTarget(vars map[string]string) (string, int) {
  401. if target, ok := vars[urlEpName]; ok {
  402. return target, byName
  403. }
  404. if target, ok := vars[urlEpID]; ok {
  405. return target, byID
  406. }
  407. // vars are populated from the URL, following cannot happen
  408. panic("Missing URL variable parameter for endpoint")
  409. }
  410. func findNetwork(c libnetwork.NetworkController, s string, by int) (libnetwork.Network, *responseStatus) {
  411. var (
  412. nw libnetwork.Network
  413. err error
  414. )
  415. switch by {
  416. case byID:
  417. nw, err = c.NetworkByID(s)
  418. case byName:
  419. nw, err = c.NetworkByName(s)
  420. default:
  421. panic(fmt.Sprintf("unexpected selector for network search: %d", by))
  422. }
  423. if err != nil {
  424. if err == libnetwork.ErrNoSuchNetwork {
  425. return nil, &responseStatus{Status: "Resource not found: Network", StatusCode: http.StatusNotFound}
  426. }
  427. return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
  428. }
  429. return nw, &successResponse
  430. }
  431. func findEndpoint(c libnetwork.NetworkController, ns, es string, nwBy, epBy int) (libnetwork.Endpoint, *responseStatus) {
  432. nw, errRsp := findNetwork(c, ns, nwBy)
  433. if !errRsp.isOK() {
  434. return nil, errRsp
  435. }
  436. var (
  437. err error
  438. ep libnetwork.Endpoint
  439. )
  440. switch epBy {
  441. case byID:
  442. ep, err = nw.EndpointByID(es)
  443. case byName:
  444. ep, err = nw.EndpointByName(es)
  445. default:
  446. panic(fmt.Sprintf("unexpected selector for endpoint search: %d", epBy))
  447. }
  448. if err != nil {
  449. if err == libnetwork.ErrNoSuchEndpoint {
  450. return nil, &responseStatus{Status: "Resource not found: Endpoint", StatusCode: http.StatusNotFound}
  451. }
  452. return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
  453. }
  454. return ep, &successResponse
  455. }
  456. func convertNetworkError(err error) *responseStatus {
  457. // No real libnetwork error => http error code conversion for now.
  458. // Will came in later when new interface for libnetwork error is vailable
  459. return &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
  460. }
  461. func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
  462. w.Header().Set("Content-Type", "application/json")
  463. w.WriteHeader(code)
  464. return json.NewEncoder(w).Encode(v)
  465. }