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