github.com/portworx/docker@v1.12.1/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 "strconv" 10 "strings" 11 12 "github.com/docker/docker/api/server/httputils" 13 "github.com/docker/docker/api/types/backend" 14 "github.com/docker/docker/pkg/ioutils" 15 "github.com/docker/docker/pkg/streamformatter" 16 "github.com/docker/docker/registry" 17 "github.com/docker/engine-api/types" 18 "github.com/docker/engine-api/types/container" 19 "github.com/docker/engine-api/types/versions" 20 "golang.org/x/net/context" 21 ) 22 23 func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 24 if err := httputils.ParseForm(r); err != nil { 25 return err 26 } 27 28 if err := httputils.CheckForJSON(r); err != nil { 29 return err 30 } 31 32 cname := r.Form.Get("container") 33 34 pause := httputils.BoolValue(r, "pause") 35 version := httputils.VersionFromContext(ctx) 36 if r.FormValue("pause") == "" && versions.GreaterThanOrEqualTo(version, "1.13") { 37 pause = true 38 } 39 40 c, _, _, err := s.decoder.DecodeConfig(r.Body) 41 if err != nil && err != io.EOF { //Do not fail if body is empty. 42 return err 43 } 44 if c == nil { 45 c = &container.Config{} 46 } 47 48 commitCfg := &backend.ContainerCommitConfig{ 49 ContainerCommitConfig: types.ContainerCommitConfig{ 50 Pause: pause, 51 Repo: r.Form.Get("repo"), 52 Tag: r.Form.Get("tag"), 53 Author: r.Form.Get("author"), 54 Comment: r.Form.Get("comment"), 55 Config: c, 56 MergeConfigs: true, 57 }, 58 Changes: r.Form["changes"], 59 } 60 61 imgID, err := s.backend.Commit(cname, commitCfg) 62 if err != nil { 63 return err 64 } 65 66 return httputils.WriteJSON(w, http.StatusCreated, &types.ContainerCommitResponse{ 67 ID: string(imgID), 68 }) 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 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 ) 85 defer output.Close() 86 87 w.Header().Set("Content-Type", "application/json") 88 89 if image != "" { //pull 90 metaHeaders := map[string][]string{} 91 for k, v := range r.Header { 92 if strings.HasPrefix(k, "X-Meta-") { 93 metaHeaders[k] = v 94 } 95 } 96 97 authEncoded := r.Header.Get("X-Registry-Auth") 98 authConfig := &types.AuthConfig{} 99 if authEncoded != "" { 100 authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) 101 if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil { 102 // for a pull it is not an error if no auth was given 103 // to increase compatibility with the existing api it is defaulting to be empty 104 authConfig = &types.AuthConfig{} 105 } 106 } 107 108 err = s.backend.PullImage(ctx, image, tag, metaHeaders, authConfig, output) 109 } else { //import 110 src := r.Form.Get("fromSrc") 111 // 'err' MUST NOT be defined within this block, we need any error 112 // generated from the download to be available to the output 113 // stream processing below 114 err = s.backend.ImportImage(src, repo, tag, message, r.Body, output, r.Form["changes"]) 115 } 116 if err != nil { 117 if !output.Flushed() { 118 return err 119 } 120 sf := streamformatter.NewJSONStreamFormatter() 121 output.Write(sf.FormatError(err)) 122 } 123 124 return nil 125 } 126 127 func (s *imageRouter) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 128 metaHeaders := map[string][]string{} 129 for k, v := range r.Header { 130 if strings.HasPrefix(k, "X-Meta-") { 131 metaHeaders[k] = v 132 } 133 } 134 if err := httputils.ParseForm(r); err != nil { 135 return err 136 } 137 authConfig := &types.AuthConfig{} 138 139 authEncoded := r.Header.Get("X-Registry-Auth") 140 if authEncoded != "" { 141 // the new format is to handle the authConfig as a header 142 authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) 143 if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil { 144 // to increase compatibility to existing api it is defaulting to be empty 145 authConfig = &types.AuthConfig{} 146 } 147 } else { 148 // the old format is supported for compatibility if there was no authConfig header 149 if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil { 150 return fmt.Errorf("Bad parameters and missing X-Registry-Auth: %v", err) 151 } 152 } 153 154 image := vars["name"] 155 tag := r.Form.Get("tag") 156 157 output := ioutils.NewWriteFlusher(w) 158 defer output.Close() 159 160 w.Header().Set("Content-Type", "application/json") 161 162 if err := s.backend.PushImage(ctx, image, tag, metaHeaders, authConfig, output); err != nil { 163 if !output.Flushed() { 164 return err 165 } 166 sf := streamformatter.NewJSONStreamFormatter() 167 output.Write(sf.FormatError(err)) 168 } 169 return nil 170 } 171 172 func (s *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 173 if err := httputils.ParseForm(r); err != nil { 174 return err 175 } 176 177 w.Header().Set("Content-Type", "application/x-tar") 178 179 output := ioutils.NewWriteFlusher(w) 180 defer output.Close() 181 var names []string 182 if name, ok := vars["name"]; ok { 183 names = []string{name} 184 } else { 185 names = r.Form["names"] 186 } 187 188 if err := s.backend.ExportImage(names, output); err != nil { 189 if !output.Flushed() { 190 return err 191 } 192 sf := streamformatter.NewJSONStreamFormatter() 193 output.Write(sf.FormatError(err)) 194 } 195 return nil 196 } 197 198 func (s *imageRouter) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 199 if err := httputils.ParseForm(r); err != nil { 200 return err 201 } 202 quiet := httputils.BoolValueOrDefault(r, "quiet", true) 203 204 if !quiet { 205 w.Header().Set("Content-Type", "application/json") 206 207 output := ioutils.NewWriteFlusher(w) 208 defer output.Close() 209 if err := s.backend.LoadImage(r.Body, output, quiet); err != nil { 210 output.Write(streamformatter.NewJSONStreamFormatter().FormatError(err)) 211 } 212 return nil 213 } 214 return s.backend.LoadImage(r.Body, w, quiet) 215 } 216 217 func (s *imageRouter) deleteImages(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 222 name := vars["name"] 223 224 if strings.TrimSpace(name) == "" { 225 return fmt.Errorf("image name cannot be blank") 226 } 227 228 force := httputils.BoolValue(r, "force") 229 prune := !httputils.BoolValue(r, "noprune") 230 231 list, err := s.backend.ImageDelete(name, force, prune) 232 if err != nil { 233 return err 234 } 235 236 return httputils.WriteJSON(w, http.StatusOK, list) 237 } 238 239 func (s *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 240 imageInspect, err := s.backend.LookupImage(vars["name"]) 241 if err != nil { 242 return err 243 } 244 245 return httputils.WriteJSON(w, http.StatusOK, imageInspect) 246 } 247 248 func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 249 if err := httputils.ParseForm(r); err != nil { 250 return err 251 } 252 253 // FIXME: The filter parameter could just be a match filter 254 images, err := s.backend.Images(r.Form.Get("filters"), r.Form.Get("filter"), httputils.BoolValue(r, "all")) 255 if err != nil { 256 return err 257 } 258 259 return httputils.WriteJSON(w, http.StatusOK, images) 260 } 261 262 func (s *imageRouter) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 263 name := vars["name"] 264 history, err := s.backend.ImageHistory(name) 265 if err != nil { 266 return err 267 } 268 269 return httputils.WriteJSON(w, http.StatusOK, history) 270 } 271 272 func (s *imageRouter) postImagesTag(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 if err := s.backend.TagImage(vars["name"], r.Form.Get("repo"), r.Form.Get("tag")); err != nil { 277 return err 278 } 279 w.WriteHeader(http.StatusCreated) 280 return nil 281 } 282 283 func (s *imageRouter) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 284 if err := httputils.ParseForm(r); err != nil { 285 return err 286 } 287 var ( 288 config *types.AuthConfig 289 authEncoded = r.Header.Get("X-Registry-Auth") 290 headers = map[string][]string{} 291 ) 292 293 if authEncoded != "" { 294 authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) 295 if err := json.NewDecoder(authJSON).Decode(&config); err != nil { 296 // for a search it is not an error if no auth was given 297 // to increase compatibility with the existing api it is defaulting to be empty 298 config = &types.AuthConfig{} 299 } 300 } 301 for k, v := range r.Header { 302 if strings.HasPrefix(k, "X-Meta-") { 303 headers[k] = v 304 } 305 } 306 limit := registry.DefaultSearchLimit 307 if r.Form.Get("limit") != "" { 308 limitValue, err := strconv.Atoi(r.Form.Get("limit")) 309 if err != nil { 310 return err 311 } 312 limit = limitValue 313 } 314 query, err := s.backend.SearchRegistryForImages(ctx, r.Form.Get("filters"), r.Form.Get("term"), limit, config, headers) 315 if err != nil { 316 return err 317 } 318 return httputils.WriteJSON(w, http.StatusOK, query.Results) 319 }