api.go 13 KB

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