cluster_routes.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. package swarm // import "github.com/docker/docker/api/server/router/swarm"
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "strconv"
  9. "github.com/docker/docker/api/server/httputils"
  10. basictypes "github.com/docker/docker/api/types"
  11. "github.com/docker/docker/api/types/backend"
  12. "github.com/docker/docker/api/types/filters"
  13. types "github.com/docker/docker/api/types/swarm"
  14. "github.com/docker/docker/api/types/versions"
  15. "github.com/docker/docker/errdefs"
  16. "github.com/pkg/errors"
  17. "github.com/sirupsen/logrus"
  18. )
  19. func (sr *swarmRouter) initCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  20. var req types.InitRequest
  21. if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
  22. if err == io.EOF {
  23. return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
  24. }
  25. return errdefs.InvalidParameter(err)
  26. }
  27. nodeID, err := sr.backend.Init(req)
  28. if err != nil {
  29. logrus.Errorf("Error initializing swarm: %v", err)
  30. return err
  31. }
  32. return httputils.WriteJSON(w, http.StatusOK, nodeID)
  33. }
  34. func (sr *swarmRouter) joinCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  35. var req types.JoinRequest
  36. if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
  37. if err == io.EOF {
  38. return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
  39. }
  40. return errdefs.InvalidParameter(err)
  41. }
  42. return sr.backend.Join(req)
  43. }
  44. func (sr *swarmRouter) leaveCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  45. if err := httputils.ParseForm(r); err != nil {
  46. return err
  47. }
  48. force := httputils.BoolValue(r, "force")
  49. return sr.backend.Leave(force)
  50. }
  51. func (sr *swarmRouter) inspectCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  52. swarm, err := sr.backend.Inspect()
  53. if err != nil {
  54. logrus.Errorf("Error getting swarm: %v", err)
  55. return err
  56. }
  57. return httputils.WriteJSON(w, http.StatusOK, swarm)
  58. }
  59. func (sr *swarmRouter) updateCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  60. var swarm types.Spec
  61. if err := json.NewDecoder(r.Body).Decode(&swarm); err != nil {
  62. if err == io.EOF {
  63. return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
  64. }
  65. return errdefs.InvalidParameter(err)
  66. }
  67. rawVersion := r.URL.Query().Get("version")
  68. version, err := strconv.ParseUint(rawVersion, 10, 64)
  69. if err != nil {
  70. err := fmt.Errorf("invalid swarm version '%s': %v", rawVersion, err)
  71. return errdefs.InvalidParameter(err)
  72. }
  73. var flags types.UpdateFlags
  74. if value := r.URL.Query().Get("rotateWorkerToken"); value != "" {
  75. rot, err := strconv.ParseBool(value)
  76. if err != nil {
  77. err := fmt.Errorf("invalid value for rotateWorkerToken: %s", value)
  78. return errdefs.InvalidParameter(err)
  79. }
  80. flags.RotateWorkerToken = rot
  81. }
  82. if value := r.URL.Query().Get("rotateManagerToken"); value != "" {
  83. rot, err := strconv.ParseBool(value)
  84. if err != nil {
  85. err := fmt.Errorf("invalid value for rotateManagerToken: %s", value)
  86. return errdefs.InvalidParameter(err)
  87. }
  88. flags.RotateManagerToken = rot
  89. }
  90. if value := r.URL.Query().Get("rotateManagerUnlockKey"); value != "" {
  91. rot, err := strconv.ParseBool(value)
  92. if err != nil {
  93. return errdefs.InvalidParameter(fmt.Errorf("invalid value for rotateManagerUnlockKey: %s", value))
  94. }
  95. flags.RotateManagerUnlockKey = rot
  96. }
  97. if err := sr.backend.Update(version, swarm, flags); err != nil {
  98. logrus.Errorf("Error configuring swarm: %v", err)
  99. return err
  100. }
  101. return nil
  102. }
  103. func (sr *swarmRouter) unlockCluster(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  104. var req types.UnlockRequest
  105. if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
  106. if err == io.EOF {
  107. return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
  108. }
  109. return errdefs.InvalidParameter(err)
  110. }
  111. if err := sr.backend.UnlockSwarm(req); err != nil {
  112. logrus.Errorf("Error unlocking swarm: %v", err)
  113. return err
  114. }
  115. return nil
  116. }
  117. func (sr *swarmRouter) getUnlockKey(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  118. unlockKey, err := sr.backend.GetUnlockKey()
  119. if err != nil {
  120. logrus.WithError(err).Errorf("Error retrieving swarm unlock key")
  121. return err
  122. }
  123. return httputils.WriteJSON(w, http.StatusOK, &basictypes.SwarmUnlockKeyResponse{
  124. UnlockKey: unlockKey,
  125. })
  126. }
  127. func (sr *swarmRouter) getServices(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  128. if err := httputils.ParseForm(r); err != nil {
  129. return err
  130. }
  131. filter, err := filters.FromJSON(r.Form.Get("filters"))
  132. if err != nil {
  133. return errdefs.InvalidParameter(err)
  134. }
  135. services, err := sr.backend.GetServices(basictypes.ServiceListOptions{Filters: filter})
  136. if err != nil {
  137. logrus.Errorf("Error getting services: %v", err)
  138. return err
  139. }
  140. return httputils.WriteJSON(w, http.StatusOK, services)
  141. }
  142. func (sr *swarmRouter) getService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  143. var insertDefaults bool
  144. if value := r.URL.Query().Get("insertDefaults"); value != "" {
  145. var err error
  146. insertDefaults, err = strconv.ParseBool(value)
  147. if err != nil {
  148. err := fmt.Errorf("invalid value for insertDefaults: %s", value)
  149. return errors.Wrapf(errdefs.InvalidParameter(err), "invalid value for insertDefaults: %s", value)
  150. }
  151. }
  152. service, err := sr.backend.GetService(vars["id"], insertDefaults)
  153. if err != nil {
  154. logrus.Errorf("Error getting service %s: %v", vars["id"], err)
  155. return err
  156. }
  157. return httputils.WriteJSON(w, http.StatusOK, service)
  158. }
  159. func (sr *swarmRouter) createService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  160. var service types.ServiceSpec
  161. if err := json.NewDecoder(r.Body).Decode(&service); err != nil {
  162. if err == io.EOF {
  163. return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
  164. }
  165. return errdefs.InvalidParameter(err)
  166. }
  167. // Get returns "" if the header does not exist
  168. encodedAuth := r.Header.Get("X-Registry-Auth")
  169. cliVersion := r.Header.Get("version")
  170. queryRegistry := false
  171. if cliVersion != "" {
  172. if versions.LessThan(cliVersion, "1.30") {
  173. queryRegistry = true
  174. }
  175. if versions.LessThan(cliVersion, "1.40") {
  176. if service.TaskTemplate.ContainerSpec != nil {
  177. // Sysctls for docker swarm services weren't supported before
  178. // API version 1.40
  179. service.TaskTemplate.ContainerSpec.Sysctls = nil
  180. }
  181. }
  182. }
  183. resp, err := sr.backend.CreateService(service, encodedAuth, queryRegistry)
  184. if err != nil {
  185. logrus.Errorf("Error creating service %s: %v", service.Name, err)
  186. return err
  187. }
  188. return httputils.WriteJSON(w, http.StatusCreated, resp)
  189. }
  190. func (sr *swarmRouter) updateService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  191. var service types.ServiceSpec
  192. if err := json.NewDecoder(r.Body).Decode(&service); err != nil {
  193. if err == io.EOF {
  194. return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
  195. }
  196. return errdefs.InvalidParameter(err)
  197. }
  198. rawVersion := r.URL.Query().Get("version")
  199. version, err := strconv.ParseUint(rawVersion, 10, 64)
  200. if err != nil {
  201. err := fmt.Errorf("invalid service version '%s': %v", rawVersion, err)
  202. return errdefs.InvalidParameter(err)
  203. }
  204. var flags basictypes.ServiceUpdateOptions
  205. // Get returns "" if the header does not exist
  206. flags.EncodedRegistryAuth = r.Header.Get("X-Registry-Auth")
  207. flags.RegistryAuthFrom = r.URL.Query().Get("registryAuthFrom")
  208. flags.Rollback = r.URL.Query().Get("rollback")
  209. cliVersion := r.Header.Get("version")
  210. queryRegistry := false
  211. if cliVersion != "" {
  212. if versions.LessThan(cliVersion, "1.30") {
  213. queryRegistry = true
  214. }
  215. if versions.LessThan(cliVersion, "1.40") {
  216. if service.TaskTemplate.ContainerSpec != nil {
  217. // Sysctls for docker swarm services weren't supported before
  218. // API version 1.40
  219. service.TaskTemplate.ContainerSpec.Sysctls = nil
  220. }
  221. }
  222. }
  223. resp, err := sr.backend.UpdateService(vars["id"], version, service, flags, queryRegistry)
  224. if err != nil {
  225. logrus.Errorf("Error updating service %s: %v", vars["id"], err)
  226. return err
  227. }
  228. return httputils.WriteJSON(w, http.StatusOK, resp)
  229. }
  230. func (sr *swarmRouter) removeService(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  231. if err := sr.backend.RemoveService(vars["id"]); err != nil {
  232. logrus.Errorf("Error removing service %s: %v", vars["id"], err)
  233. return err
  234. }
  235. return nil
  236. }
  237. func (sr *swarmRouter) getTaskLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  238. if err := httputils.ParseForm(r); err != nil {
  239. return err
  240. }
  241. // make a selector to pass to the helper function
  242. selector := &backend.LogSelector{
  243. Tasks: []string{vars["id"]},
  244. }
  245. return sr.swarmLogs(ctx, w, r, selector)
  246. }
  247. func (sr *swarmRouter) getServiceLogs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  248. if err := httputils.ParseForm(r); err != nil {
  249. return err
  250. }
  251. // make a selector to pass to the helper function
  252. selector := &backend.LogSelector{
  253. Services: []string{vars["id"]},
  254. }
  255. return sr.swarmLogs(ctx, w, r, selector)
  256. }
  257. func (sr *swarmRouter) getNodes(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  258. if err := httputils.ParseForm(r); err != nil {
  259. return err
  260. }
  261. filter, err := filters.FromJSON(r.Form.Get("filters"))
  262. if err != nil {
  263. return err
  264. }
  265. nodes, err := sr.backend.GetNodes(basictypes.NodeListOptions{Filters: filter})
  266. if err != nil {
  267. logrus.Errorf("Error getting nodes: %v", err)
  268. return err
  269. }
  270. return httputils.WriteJSON(w, http.StatusOK, nodes)
  271. }
  272. func (sr *swarmRouter) getNode(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  273. node, err := sr.backend.GetNode(vars["id"])
  274. if err != nil {
  275. logrus.Errorf("Error getting node %s: %v", vars["id"], err)
  276. return err
  277. }
  278. return httputils.WriteJSON(w, http.StatusOK, node)
  279. }
  280. func (sr *swarmRouter) updateNode(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  281. var node types.NodeSpec
  282. if err := json.NewDecoder(r.Body).Decode(&node); err != nil {
  283. if err == io.EOF {
  284. return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
  285. }
  286. return errdefs.InvalidParameter(err)
  287. }
  288. rawVersion := r.URL.Query().Get("version")
  289. version, err := strconv.ParseUint(rawVersion, 10, 64)
  290. if err != nil {
  291. err := fmt.Errorf("invalid node version '%s': %v", rawVersion, err)
  292. return errdefs.InvalidParameter(err)
  293. }
  294. if err := sr.backend.UpdateNode(vars["id"], version, node); err != nil {
  295. logrus.Errorf("Error updating node %s: %v", vars["id"], err)
  296. return err
  297. }
  298. return nil
  299. }
  300. func (sr *swarmRouter) removeNode(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  301. if err := httputils.ParseForm(r); err != nil {
  302. return err
  303. }
  304. force := httputils.BoolValue(r, "force")
  305. if err := sr.backend.RemoveNode(vars["id"], force); err != nil {
  306. logrus.Errorf("Error removing node %s: %v", vars["id"], err)
  307. return err
  308. }
  309. return nil
  310. }
  311. func (sr *swarmRouter) getTasks(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  312. if err := httputils.ParseForm(r); err != nil {
  313. return err
  314. }
  315. filter, err := filters.FromJSON(r.Form.Get("filters"))
  316. if err != nil {
  317. return err
  318. }
  319. tasks, err := sr.backend.GetTasks(basictypes.TaskListOptions{Filters: filter})
  320. if err != nil {
  321. logrus.Errorf("Error getting tasks: %v", err)
  322. return err
  323. }
  324. return httputils.WriteJSON(w, http.StatusOK, tasks)
  325. }
  326. func (sr *swarmRouter) getTask(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  327. task, err := sr.backend.GetTask(vars["id"])
  328. if err != nil {
  329. logrus.Errorf("Error getting task %s: %v", vars["id"], err)
  330. return err
  331. }
  332. return httputils.WriteJSON(w, http.StatusOK, task)
  333. }
  334. func (sr *swarmRouter) getSecrets(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  335. if err := httputils.ParseForm(r); err != nil {
  336. return err
  337. }
  338. filters, err := filters.FromJSON(r.Form.Get("filters"))
  339. if err != nil {
  340. return err
  341. }
  342. secrets, err := sr.backend.GetSecrets(basictypes.SecretListOptions{Filters: filters})
  343. if err != nil {
  344. return err
  345. }
  346. return httputils.WriteJSON(w, http.StatusOK, secrets)
  347. }
  348. func (sr *swarmRouter) createSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  349. var secret types.SecretSpec
  350. if err := json.NewDecoder(r.Body).Decode(&secret); err != nil {
  351. if err == io.EOF {
  352. return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
  353. }
  354. return errdefs.InvalidParameter(err)
  355. }
  356. version := httputils.VersionFromContext(ctx)
  357. if secret.Templating != nil && versions.LessThan(version, "1.37") {
  358. return errdefs.InvalidParameter(errors.Errorf("secret templating is not supported on the specified API version: %s", version))
  359. }
  360. id, err := sr.backend.CreateSecret(secret)
  361. if err != nil {
  362. return err
  363. }
  364. return httputils.WriteJSON(w, http.StatusCreated, &basictypes.SecretCreateResponse{
  365. ID: id,
  366. })
  367. }
  368. func (sr *swarmRouter) removeSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  369. if err := sr.backend.RemoveSecret(vars["id"]); err != nil {
  370. return err
  371. }
  372. w.WriteHeader(http.StatusNoContent)
  373. return nil
  374. }
  375. func (sr *swarmRouter) getSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  376. secret, err := sr.backend.GetSecret(vars["id"])
  377. if err != nil {
  378. return err
  379. }
  380. return httputils.WriteJSON(w, http.StatusOK, secret)
  381. }
  382. func (sr *swarmRouter) updateSecret(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  383. var secret types.SecretSpec
  384. if err := json.NewDecoder(r.Body).Decode(&secret); err != nil {
  385. if err == io.EOF {
  386. return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
  387. }
  388. return errdefs.InvalidParameter(err)
  389. }
  390. rawVersion := r.URL.Query().Get("version")
  391. version, err := strconv.ParseUint(rawVersion, 10, 64)
  392. if err != nil {
  393. return errdefs.InvalidParameter(fmt.Errorf("invalid secret version"))
  394. }
  395. id := vars["id"]
  396. return sr.backend.UpdateSecret(id, version, secret)
  397. }
  398. func (sr *swarmRouter) getConfigs(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  399. if err := httputils.ParseForm(r); err != nil {
  400. return err
  401. }
  402. filters, err := filters.FromJSON(r.Form.Get("filters"))
  403. if err != nil {
  404. return err
  405. }
  406. configs, err := sr.backend.GetConfigs(basictypes.ConfigListOptions{Filters: filters})
  407. if err != nil {
  408. return err
  409. }
  410. return httputils.WriteJSON(w, http.StatusOK, configs)
  411. }
  412. func (sr *swarmRouter) createConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  413. var config types.ConfigSpec
  414. if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
  415. if err == io.EOF {
  416. return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
  417. }
  418. return errdefs.InvalidParameter(err)
  419. }
  420. version := httputils.VersionFromContext(ctx)
  421. if config.Templating != nil && versions.LessThan(version, "1.37") {
  422. return errdefs.InvalidParameter(errors.Errorf("config templating is not supported on the specified API version: %s", version))
  423. }
  424. id, err := sr.backend.CreateConfig(config)
  425. if err != nil {
  426. return err
  427. }
  428. return httputils.WriteJSON(w, http.StatusCreated, &basictypes.ConfigCreateResponse{
  429. ID: id,
  430. })
  431. }
  432. func (sr *swarmRouter) removeConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  433. if err := sr.backend.RemoveConfig(vars["id"]); err != nil {
  434. return err
  435. }
  436. w.WriteHeader(http.StatusNoContent)
  437. return nil
  438. }
  439. func (sr *swarmRouter) getConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  440. config, err := sr.backend.GetConfig(vars["id"])
  441. if err != nil {
  442. return err
  443. }
  444. return httputils.WriteJSON(w, http.StatusOK, config)
  445. }
  446. func (sr *swarmRouter) updateConfig(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  447. var config types.ConfigSpec
  448. if err := json.NewDecoder(r.Body).Decode(&config); err != nil {
  449. if err == io.EOF {
  450. return errdefs.InvalidParameter(errors.New("got EOF while reading request body"))
  451. }
  452. return errdefs.InvalidParameter(err)
  453. }
  454. rawVersion := r.URL.Query().Get("version")
  455. version, err := strconv.ParseUint(rawVersion, 10, 64)
  456. if err != nil {
  457. return errdefs.InvalidParameter(fmt.Errorf("invalid config version"))
  458. }
  459. id := vars["id"]
  460. return sr.backend.UpdateConfig(id, version, config)
  461. }