image_routes.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. package image
  2. import (
  3. "encoding/base64"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "strconv"
  9. "strings"
  10. "github.com/docker/docker/api/server/httputils"
  11. "github.com/docker/docker/api/types"
  12. "github.com/docker/docker/api/types/backend"
  13. "github.com/docker/docker/api/types/container"
  14. "github.com/docker/docker/api/types/filters"
  15. "github.com/docker/docker/api/types/versions"
  16. "github.com/docker/docker/pkg/ioutils"
  17. "github.com/docker/docker/pkg/streamformatter"
  18. "github.com/docker/docker/registry"
  19. "golang.org/x/net/context"
  20. )
  21. func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  22. if err := httputils.ParseForm(r); err != nil {
  23. return err
  24. }
  25. if err := httputils.CheckForJSON(r); err != nil {
  26. return err
  27. }
  28. cname := r.Form.Get("container")
  29. pause := httputils.BoolValue(r, "pause")
  30. version := httputils.VersionFromContext(ctx)
  31. if r.FormValue("pause") == "" && versions.GreaterThanOrEqualTo(version, "1.13") {
  32. pause = true
  33. }
  34. c, _, _, err := s.decoder.DecodeConfig(r.Body)
  35. if err != nil && err != io.EOF { //Do not fail if body is empty.
  36. return err
  37. }
  38. if c == nil {
  39. c = &container.Config{}
  40. }
  41. commitCfg := &backend.ContainerCommitConfig{
  42. ContainerCommitConfig: types.ContainerCommitConfig{
  43. Pause: pause,
  44. Repo: r.Form.Get("repo"),
  45. Tag: r.Form.Get("tag"),
  46. Author: r.Form.Get("author"),
  47. Comment: r.Form.Get("comment"),
  48. Config: c,
  49. MergeConfigs: true,
  50. },
  51. Changes: r.Form["changes"],
  52. }
  53. imgID, err := s.backend.Commit(cname, commitCfg)
  54. if err != nil {
  55. return err
  56. }
  57. return httputils.WriteJSON(w, http.StatusCreated, &types.IDResponse{
  58. ID: string(imgID),
  59. })
  60. }
  61. // Creates an image from Pull or from Import
  62. func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  63. if err := httputils.ParseForm(r); err != nil {
  64. return err
  65. }
  66. var (
  67. image = r.Form.Get("fromImage")
  68. repo = r.Form.Get("repo")
  69. tag = r.Form.Get("tag")
  70. message = r.Form.Get("message")
  71. err error
  72. output = ioutils.NewWriteFlusher(w)
  73. )
  74. defer output.Close()
  75. w.Header().Set("Content-Type", "application/json")
  76. if image != "" { //pull
  77. metaHeaders := map[string][]string{}
  78. for k, v := range r.Header {
  79. if strings.HasPrefix(k, "X-Meta-") {
  80. metaHeaders[k] = v
  81. }
  82. }
  83. authEncoded := r.Header.Get("X-Registry-Auth")
  84. authConfig := &types.AuthConfig{}
  85. if authEncoded != "" {
  86. authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
  87. if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
  88. // for a pull it is not an error if no auth was given
  89. // to increase compatibility with the existing api it is defaulting to be empty
  90. authConfig = &types.AuthConfig{}
  91. }
  92. }
  93. err = s.backend.PullImage(ctx, image, tag, metaHeaders, authConfig, output)
  94. } else { //import
  95. src := r.Form.Get("fromSrc")
  96. // 'err' MUST NOT be defined within this block, we need any error
  97. // generated from the download to be available to the output
  98. // stream processing below
  99. err = s.backend.ImportImage(src, repo, tag, message, r.Body, output, r.Form["changes"])
  100. }
  101. if err != nil {
  102. if !output.Flushed() {
  103. return err
  104. }
  105. output.Write(streamformatter.FormatError(err))
  106. }
  107. return nil
  108. }
  109. func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  110. metaHeaders := map[string][]string{}
  111. for k, v := range r.Header {
  112. if strings.HasPrefix(k, "X-Meta-") {
  113. metaHeaders[k] = v
  114. }
  115. }
  116. if err := httputils.ParseForm(r); err != nil {
  117. return err
  118. }
  119. authConfig := &types.AuthConfig{}
  120. authEncoded := r.Header.Get("X-Registry-Auth")
  121. if authEncoded != "" {
  122. // the new format is to handle the authConfig as a header
  123. authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
  124. if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
  125. // to increase compatibility to existing api it is defaulting to be empty
  126. authConfig = &types.AuthConfig{}
  127. }
  128. } else {
  129. // the old format is supported for compatibility if there was no authConfig header
  130. if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
  131. return fmt.Errorf("Bad parameters and missing X-Registry-Auth: %v", err)
  132. }
  133. }
  134. image := vars["name"]
  135. tag := r.Form.Get("tag")
  136. output := ioutils.NewWriteFlusher(w)
  137. defer output.Close()
  138. w.Header().Set("Content-Type", "application/json")
  139. if err := s.backend.PushImage(ctx, image, tag, metaHeaders, authConfig, output); err != nil {
  140. if !output.Flushed() {
  141. return err
  142. }
  143. output.Write(streamformatter.FormatError(err))
  144. }
  145. return nil
  146. }
  147. func (s *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  148. if err := httputils.ParseForm(r); err != nil {
  149. return err
  150. }
  151. w.Header().Set("Content-Type", "application/x-tar")
  152. output := ioutils.NewWriteFlusher(w)
  153. defer output.Close()
  154. var names []string
  155. if name, ok := vars["name"]; ok {
  156. names = []string{name}
  157. } else {
  158. names = r.Form["names"]
  159. }
  160. if err := s.backend.ExportImage(names, output); err != nil {
  161. if !output.Flushed() {
  162. return err
  163. }
  164. output.Write(streamformatter.FormatError(err))
  165. }
  166. return nil
  167. }
  168. func (s *imageRouter) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  169. if err := httputils.ParseForm(r); err != nil {
  170. return err
  171. }
  172. quiet := httputils.BoolValueOrDefault(r, "quiet", true)
  173. w.Header().Set("Content-Type", "application/json")
  174. output := ioutils.NewWriteFlusher(w)
  175. defer output.Close()
  176. if err := s.backend.LoadImage(r.Body, output, quiet); err != nil {
  177. output.Write(streamformatter.FormatError(err))
  178. }
  179. return nil
  180. }
  181. func (s *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  182. if err := httputils.ParseForm(r); err != nil {
  183. return err
  184. }
  185. name := vars["name"]
  186. if strings.TrimSpace(name) == "" {
  187. return fmt.Errorf("image name cannot be blank")
  188. }
  189. force := httputils.BoolValue(r, "force")
  190. prune := !httputils.BoolValue(r, "noprune")
  191. list, err := s.backend.ImageDelete(name, force, prune)
  192. if err != nil {
  193. return err
  194. }
  195. return httputils.WriteJSON(w, http.StatusOK, list)
  196. }
  197. func (s *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  198. imageInspect, err := s.backend.LookupImage(vars["name"])
  199. if err != nil {
  200. return err
  201. }
  202. return httputils.WriteJSON(w, http.StatusOK, imageInspect)
  203. }
  204. func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  205. if err := httputils.ParseForm(r); err != nil {
  206. return err
  207. }
  208. imageFilters, err := filters.FromParam(r.Form.Get("filters"))
  209. if err != nil {
  210. return err
  211. }
  212. filterParam := r.Form.Get("filter")
  213. // FIXME(vdemeester) This has been deprecated in 1.13, and is target for removal for v17.12
  214. if filterParam != "" {
  215. imageFilters.Add("reference", filterParam)
  216. }
  217. images, err := s.backend.Images(imageFilters, httputils.BoolValue(r, "all"), false)
  218. if err != nil {
  219. return err
  220. }
  221. return httputils.WriteJSON(w, http.StatusOK, images)
  222. }
  223. func (s *imageRouter) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  224. name := vars["name"]
  225. history, err := s.backend.ImageHistory(name)
  226. if err != nil {
  227. return err
  228. }
  229. return httputils.WriteJSON(w, http.StatusOK, history)
  230. }
  231. func (s *imageRouter) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  232. if err := httputils.ParseForm(r); err != nil {
  233. return err
  234. }
  235. if err := s.backend.TagImage(vars["name"], r.Form.Get("repo"), r.Form.Get("tag")); err != nil {
  236. return err
  237. }
  238. w.WriteHeader(http.StatusCreated)
  239. return nil
  240. }
  241. func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  242. if err := httputils.ParseForm(r); err != nil {
  243. return err
  244. }
  245. var (
  246. config *types.AuthConfig
  247. authEncoded = r.Header.Get("X-Registry-Auth")
  248. headers = map[string][]string{}
  249. )
  250. if authEncoded != "" {
  251. authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
  252. if err := json.NewDecoder(authJSON).Decode(&config); err != nil {
  253. // for a search it is not an error if no auth was given
  254. // to increase compatibility with the existing api it is defaulting to be empty
  255. config = &types.AuthConfig{}
  256. }
  257. }
  258. for k, v := range r.Header {
  259. if strings.HasPrefix(k, "X-Meta-") {
  260. headers[k] = v
  261. }
  262. }
  263. limit := registry.DefaultSearchLimit
  264. if r.Form.Get("limit") != "" {
  265. limitValue, err := strconv.Atoi(r.Form.Get("limit"))
  266. if err != nil {
  267. return err
  268. }
  269. limit = limitValue
  270. }
  271. query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("filters"), r.Form.Get("term"), limit, config, headers)
  272. if err != nil {
  273. return err
  274. }
  275. return httputils.WriteJSON(w, http.StatusOK, query.Results)
  276. }
  277. func (s *imageRouter) postImagesPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  278. if err := httputils.ParseForm(r); err != nil {
  279. return err
  280. }
  281. pruneFilters, err := filters.FromParam(r.Form.Get("filters"))
  282. if err != nil {
  283. return err
  284. }
  285. pruneReport, err := s.backend.ImagesPrune(ctx, pruneFilters)
  286. if err != nil {
  287. return err
  288. }
  289. return httputils.WriteJSON(w, http.StatusOK, pruneReport)
  290. }