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