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