github.com/rhatdan/docker@v0.7.7-0.20180119204836-47a0dcbcd20a/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 "os" 10 "strconv" 11 "strings" 12 13 "github.com/docker/docker/api/server/httputils" 14 "github.com/docker/docker/api/types" 15 "github.com/docker/docker/api/types/backend" 16 "github.com/docker/docker/api/types/filters" 17 "github.com/docker/docker/api/types/versions" 18 "github.com/docker/docker/errdefs" 19 "github.com/docker/docker/pkg/ioutils" 20 "github.com/docker/docker/pkg/streamformatter" 21 "github.com/docker/docker/pkg/system" 22 "github.com/docker/docker/registry" 23 specs "github.com/opencontainers/image-spec/specs-go/v1" 24 "github.com/pkg/errors" 25 "golang.org/x/net/context" 26 ) 27 28 func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 29 if err := httputils.ParseForm(r); err != nil { 30 return err 31 } 32 33 if err := httputils.CheckForJSON(r); err != nil { 34 return err 35 } 36 37 cname := r.Form.Get("container") 38 39 pause := httputils.BoolValue(r, "pause") 40 version := httputils.VersionFromContext(ctx) 41 if r.FormValue("pause") == "" && versions.GreaterThanOrEqualTo(version, "1.13") { 42 pause = true 43 } 44 45 c, _, _, err := s.decoder.DecodeConfig(r.Body) 46 if err != nil && err != io.EOF { //Do not fail if body is empty. 47 return err 48 } 49 50 commitCfg := &backend.ContainerCommitConfig{ 51 ContainerCommitConfig: types.ContainerCommitConfig{ 52 Pause: pause, 53 Repo: r.Form.Get("repo"), 54 Tag: r.Form.Get("tag"), 55 Author: r.Form.Get("author"), 56 Comment: r.Form.Get("comment"), 57 Config: c, 58 MergeConfigs: true, 59 }, 60 Changes: r.Form["changes"], 61 } 62 63 imgID, err := s.backend.Commit(cname, commitCfg) 64 if err != nil { 65 return err 66 } 67 68 return httputils.WriteJSON(w, http.StatusCreated, &types.IDResponse{ID: imgID}) 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 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 platform = &specs.Platform{} 86 ) 87 defer output.Close() 88 89 w.Header().Set("Content-Type", "application/json") 90 91 version := httputils.VersionFromContext(ctx) 92 if versions.GreaterThanOrEqualTo(version, "1.32") { 93 // TODO @jhowardmsft. The following environment variable is an interim 94 // measure to allow the daemon to have a default platform if omitted by 95 // the client. This allows LCOW and WCOW to work with a down-level CLI 96 // for a short period of time, as the CLI changes can't be merged 97 // until after the daemon changes have been merged. Once the CLI is 98 // updated, this can be removed. PR for CLI is currently in 99 // https://github.com/docker/cli/pull/474. 100 apiPlatform := r.FormValue("platform") 101 if system.LCOWSupported() && apiPlatform == "" { 102 apiPlatform = os.Getenv("LCOW_API_PLATFORM_IF_OMITTED") 103 } 104 platform = system.ParsePlatform(apiPlatform) 105 if err = system.ValidatePlatform(platform); err != nil { 106 err = fmt.Errorf("invalid platform: %s", err) 107 } 108 } 109 110 if err == nil { 111 if image != "" { //pull 112 metaHeaders := map[string][]string{} 113 for k, v := range r.Header { 114 if strings.HasPrefix(k, "X-Meta-") { 115 metaHeaders[k] = v 116 } 117 } 118 119 authEncoded := r.Header.Get("X-Registry-Auth") 120 authConfig := &types.AuthConfig{} 121 if authEncoded != "" { 122 authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) 123 if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil { 124 // for a pull it is not an error if no auth was given 125 // to increase compatibility with the existing api it is defaulting to be empty 126 authConfig = &types.AuthConfig{} 127 } 128 } 129 err = s.backend.PullImage(ctx, image, tag, platform.OS, metaHeaders, authConfig, output) 130 } else { //import 131 src := r.Form.Get("fromSrc") 132 // 'err' MUST NOT be defined within this block, we need any error 133 // generated from the download to be available to the output 134 // stream processing below 135 err = s.backend.ImportImage(src, repo, platform.OS, tag, message, r.Body, output, r.Form["changes"]) 136 } 137 } 138 if err != nil { 139 if !output.Flushed() { 140 return err 141 } 142 output.Write(streamformatter.FormatError(err)) 143 } 144 145 return nil 146 } 147 148 func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 149 metaHeaders := map[string][]string{} 150 for k, v := range r.Header { 151 if strings.HasPrefix(k, "X-Meta-") { 152 metaHeaders[k] = v 153 } 154 } 155 if err := httputils.ParseForm(r); err != nil { 156 return err 157 } 158 authConfig := &types.AuthConfig{} 159 160 authEncoded := r.Header.Get("X-Registry-Auth") 161 if authEncoded != "" { 162 // the new format is to handle the authConfig as a header 163 authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) 164 if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil { 165 // to increase compatibility to existing api it is defaulting to be empty 166 authConfig = &types.AuthConfig{} 167 } 168 } else { 169 // the old format is supported for compatibility if there was no authConfig header 170 if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil { 171 return errors.Wrap(errdefs.InvalidParameter(err), "Bad parameters and missing X-Registry-Auth") 172 } 173 } 174 175 image := vars["name"] 176 tag := r.Form.Get("tag") 177 178 output := ioutils.NewWriteFlusher(w) 179 defer output.Close() 180 181 w.Header().Set("Content-Type", "application/json") 182 183 if err := s.backend.PushImage(ctx, image, tag, metaHeaders, authConfig, output); err != nil { 184 if !output.Flushed() { 185 return err 186 } 187 output.Write(streamformatter.FormatError(err)) 188 } 189 return nil 190 } 191 192 func (s *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 193 if err := httputils.ParseForm(r); err != nil { 194 return err 195 } 196 197 w.Header().Set("Content-Type", "application/x-tar") 198 199 output := ioutils.NewWriteFlusher(w) 200 defer output.Close() 201 var names []string 202 if name, ok := vars["name"]; ok { 203 names = []string{name} 204 } else { 205 names = r.Form["names"] 206 } 207 208 if err := s.backend.ExportImage(names, output); err != nil { 209 if !output.Flushed() { 210 return err 211 } 212 output.Write(streamformatter.FormatError(err)) 213 } 214 return nil 215 } 216 217 func (s *imageRouter) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 218 if err := httputils.ParseForm(r); err != nil { 219 return err 220 } 221 quiet := httputils.BoolValueOrDefault(r, "quiet", true) 222 223 w.Header().Set("Content-Type", "application/json") 224 225 output := ioutils.NewWriteFlusher(w) 226 defer output.Close() 227 if err := s.backend.LoadImage(r.Body, output, quiet); err != nil { 228 output.Write(streamformatter.FormatError(err)) 229 } 230 return nil 231 } 232 233 type missingImageError struct{} 234 235 func (missingImageError) Error() string { 236 return "image name cannot be blank" 237 } 238 239 func (missingImageError) InvalidParameter() {} 240 241 func (s *imageRouter) deleteImages(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 246 name := vars["name"] 247 248 if strings.TrimSpace(name) == "" { 249 return missingImageError{} 250 } 251 252 force := httputils.BoolValue(r, "force") 253 prune := !httputils.BoolValue(r, "noprune") 254 255 list, err := s.backend.ImageDelete(name, force, prune) 256 if err != nil { 257 return err 258 } 259 260 return httputils.WriteJSON(w, http.StatusOK, list) 261 } 262 263 func (s *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 264 imageInspect, err := s.backend.LookupImage(vars["name"]) 265 if err != nil { 266 return err 267 } 268 269 return httputils.WriteJSON(w, http.StatusOK, imageInspect) 270 } 271 272 func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 273 if err := httputils.ParseForm(r); err != nil { 274 return err 275 } 276 277 imageFilters, err := filters.FromJSON(r.Form.Get("filters")) 278 if err != nil { 279 return err 280 } 281 282 filterParam := r.Form.Get("filter") 283 // FIXME(vdemeester) This has been deprecated in 1.13, and is target for removal for v17.12 284 if filterParam != "" { 285 imageFilters.Add("reference", filterParam) 286 } 287 288 images, err := s.backend.Images(imageFilters, httputils.BoolValue(r, "all"), false) 289 if err != nil { 290 return err 291 } 292 293 return httputils.WriteJSON(w, http.StatusOK, images) 294 } 295 296 func (s *imageRouter) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 297 name := vars["name"] 298 history, err := s.backend.ImageHistory(name) 299 if err != nil { 300 return err 301 } 302 303 return httputils.WriteJSON(w, http.StatusOK, history) 304 } 305 306 func (s *imageRouter) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 307 if err := httputils.ParseForm(r); err != nil { 308 return err 309 } 310 if err := s.backend.TagImage(vars["name"], r.Form.Get("repo"), r.Form.Get("tag")); err != nil { 311 return err 312 } 313 w.WriteHeader(http.StatusCreated) 314 return nil 315 } 316 317 func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 318 if err := httputils.ParseForm(r); err != nil { 319 return err 320 } 321 var ( 322 config *types.AuthConfig 323 authEncoded = r.Header.Get("X-Registry-Auth") 324 headers = map[string][]string{} 325 ) 326 327 if authEncoded != "" { 328 authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) 329 if err := json.NewDecoder(authJSON).Decode(&config); err != nil { 330 // for a search it is not an error if no auth was given 331 // to increase compatibility with the existing api it is defaulting to be empty 332 config = &types.AuthConfig{} 333 } 334 } 335 for k, v := range r.Header { 336 if strings.HasPrefix(k, "X-Meta-") { 337 headers[k] = v 338 } 339 } 340 limit := registry.DefaultSearchLimit 341 if r.Form.Get("limit") != "" { 342 limitValue, err := strconv.Atoi(r.Form.Get("limit")) 343 if err != nil { 344 return err 345 } 346 limit = limitValue 347 } 348 query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("filters"), r.Form.Get("term"), limit, config, headers) 349 if err != nil { 350 return err 351 } 352 return httputils.WriteJSON(w, http.StatusOK, query.Results) 353 } 354 355 func (s *imageRouter) postImagesPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 356 if err := httputils.ParseForm(r); err != nil { 357 return err 358 } 359 360 pruneFilters, err := filters.FromJSON(r.Form.Get("filters")) 361 if err != nil { 362 return err 363 } 364 365 pruneReport, err := s.backend.ImagesPrune(ctx, pruneFilters) 366 if err != nil { 367 return err 368 } 369 return httputils.WriteJSON(w, http.StatusOK, pruneReport) 370 }