image_routes.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  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. sf := streamformatter.NewJSONStreamFormatter()
  106. output.Write(sf.FormatError(err))
  107. }
  108. return nil
  109. }
  110. func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  111. metaHeaders := map[string][]string{}
  112. for k, v := range r.Header {
  113. if strings.HasPrefix(k, "X-Meta-") {
  114. metaHeaders[k] = v
  115. }
  116. }
  117. if err := httputils.ParseForm(r); err != nil {
  118. return err
  119. }
  120. authConfig := &types.AuthConfig{}
  121. authEncoded := r.Header.Get("X-Registry-Auth")
  122. if authEncoded != "" {
  123. // the new format is to handle the authConfig as a header
  124. authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
  125. if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
  126. // to increase compatibility to existing api it is defaulting to be empty
  127. authConfig = &types.AuthConfig{}
  128. }
  129. } else {
  130. // the old format is supported for compatibility if there was no authConfig header
  131. if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
  132. return fmt.Errorf("Bad parameters and missing X-Registry-Auth: %v", err)
  133. }
  134. }
  135. image := vars["name"]
  136. tag := r.Form.Get("tag")
  137. output := ioutils.NewWriteFlusher(w)
  138. defer output.Close()
  139. w.Header().Set("Content-Type", "application/json")
  140. if err := s.backend.PushImage(ctx, image, tag, metaHeaders, authConfig, output); err != nil {
  141. if !output.Flushed() {
  142. return err
  143. }
  144. sf := streamformatter.NewJSONStreamFormatter()
  145. output.Write(sf.FormatError(err))
  146. }
  147. return nil
  148. }
  149. func (s *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  150. if err := httputils.ParseForm(r); err != nil {
  151. return err
  152. }
  153. w.Header().Set("Content-Type", "application/x-tar")
  154. output := ioutils.NewWriteFlusher(w)
  155. defer output.Close()
  156. var names []string
  157. if name, ok := vars["name"]; ok {
  158. names = []string{name}
  159. } else {
  160. names = r.Form["names"]
  161. }
  162. if err := s.backend.ExportImage(names, output); err != nil {
  163. if !output.Flushed() {
  164. return err
  165. }
  166. sf := streamformatter.NewJSONStreamFormatter()
  167. output.Write(sf.FormatError(err))
  168. }
  169. return nil
  170. }
  171. func (s *imageRouter) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  172. if err := httputils.ParseForm(r); err != nil {
  173. return err
  174. }
  175. quiet := httputils.BoolValueOrDefault(r, "quiet", true)
  176. w.Header().Set("Content-Type", "application/json")
  177. output := ioutils.NewWriteFlusher(w)
  178. defer output.Close()
  179. if err := s.backend.LoadImage(r.Body, output, quiet); err != nil {
  180. output.Write(streamformatter.NewJSONStreamFormatter().FormatError(err))
  181. }
  182. return nil
  183. }
  184. func (s *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  185. if err := httputils.ParseForm(r); err != nil {
  186. return err
  187. }
  188. name := vars["name"]
  189. if strings.TrimSpace(name) == "" {
  190. return fmt.Errorf("image name cannot be blank")
  191. }
  192. force := httputils.BoolValue(r, "force")
  193. prune := !httputils.BoolValue(r, "noprune")
  194. list, err := s.backend.ImageDelete(name, force, prune)
  195. if err != nil {
  196. return err
  197. }
  198. return httputils.WriteJSON(w, http.StatusOK, list)
  199. }
  200. func (s *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  201. imageInspect, err := s.backend.LookupImage(vars["name"])
  202. if err != nil {
  203. return err
  204. }
  205. return httputils.WriteJSON(w, http.StatusOK, imageInspect)
  206. }
  207. func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  208. if err := httputils.ParseForm(r); err != nil {
  209. return err
  210. }
  211. imageFilters, err := filters.FromParam(r.Form.Get("filters"))
  212. if err != nil {
  213. return err
  214. }
  215. version := httputils.VersionFromContext(ctx)
  216. filterParam := r.Form.Get("filter")
  217. if versions.LessThan(version, "1.28") && filterParam != "" {
  218. imageFilters.Add("reference", filterParam)
  219. }
  220. images, err := s.backend.Images(imageFilters, httputils.BoolValue(r, "all"), false)
  221. if err != nil {
  222. return err
  223. }
  224. return httputils.WriteJSON(w, http.StatusOK, images)
  225. }
  226. func (s *imageRouter) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  227. name := vars["name"]
  228. history, err := s.backend.ImageHistory(name)
  229. if err != nil {
  230. return err
  231. }
  232. return httputils.WriteJSON(w, http.StatusOK, history)
  233. }
  234. func (s *imageRouter) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  235. if err := httputils.ParseForm(r); err != nil {
  236. return err
  237. }
  238. if err := s.backend.TagImage(vars["name"], r.Form.Get("repo"), r.Form.Get("tag")); err != nil {
  239. return err
  240. }
  241. w.WriteHeader(http.StatusCreated)
  242. return nil
  243. }
  244. func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  245. if err := httputils.ParseForm(r); err != nil {
  246. return err
  247. }
  248. var (
  249. config *types.AuthConfig
  250. authEncoded = r.Header.Get("X-Registry-Auth")
  251. headers = map[string][]string{}
  252. )
  253. if authEncoded != "" {
  254. authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
  255. if err := json.NewDecoder(authJSON).Decode(&config); err != nil {
  256. // for a search it is not an error if no auth was given
  257. // to increase compatibility with the existing api it is defaulting to be empty
  258. config = &types.AuthConfig{}
  259. }
  260. }
  261. for k, v := range r.Header {
  262. if strings.HasPrefix(k, "X-Meta-") {
  263. headers[k] = v
  264. }
  265. }
  266. limit := registry.DefaultSearchLimit
  267. if r.Form.Get("limit") != "" {
  268. limitValue, err := strconv.Atoi(r.Form.Get("limit"))
  269. if err != nil {
  270. return err
  271. }
  272. limit = limitValue
  273. }
  274. query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("filters"), r.Form.Get("term"), limit, config, headers)
  275. if err != nil {
  276. return err
  277. }
  278. return httputils.WriteJSON(w, http.StatusOK, query.Results)
  279. }
  280. func (s *imageRouter) postImagesPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  281. if err := httputils.ParseForm(r); err != nil {
  282. return err
  283. }
  284. if err := httputils.CheckForJSON(r); err != nil {
  285. return err
  286. }
  287. var cfg types.ImagesPruneConfig
  288. if err := json.NewDecoder(r.Body).Decode(&cfg); err != nil {
  289. return err
  290. }
  291. pruneReport, err := s.backend.ImagesPrune(&cfg)
  292. if err != nil {
  293. return err
  294. }
  295. return httputils.WriteJSON(w, http.StatusOK, pruneReport)
  296. }