github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/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 version := httputils.VersionFromContext(ctx) 235 if versions.LessThan(version, "1.41") { 236 filterParam := r.Form.Get("filter") 237 if filterParam != "" { 238 imageFilters.Add("reference", filterParam) 239 } 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 }