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