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