github.com/endocode/docker@v1.4.2-0.20160113120958-46eb4700391e/api/server/router/local/image.go (about) 1 package local 2 3 import ( 4 "encoding/base64" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "net/http" 10 "strings" 11 12 "github.com/docker/distribution/digest" 13 "github.com/docker/docker/api/server/httputils" 14 "github.com/docker/docker/builder/dockerfile" 15 derr "github.com/docker/docker/errors" 16 "github.com/docker/docker/pkg/ioutils" 17 "github.com/docker/docker/pkg/streamformatter" 18 "github.com/docker/docker/reference" 19 "github.com/docker/docker/runconfig" 20 "github.com/docker/engine-api/types" 21 "github.com/docker/engine-api/types/container" 22 "golang.org/x/net/context" 23 ) 24 25 func (s *router) postCommit(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 26 if err := httputils.ParseForm(r); err != nil { 27 return err 28 } 29 30 if err := httputils.CheckForJSON(r); err != nil { 31 return err 32 } 33 34 cname := r.Form.Get("container") 35 36 pause := httputils.BoolValue(r, "pause") 37 version := httputils.VersionFromContext(ctx) 38 if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") { 39 pause = true 40 } 41 42 c, _, _, err := runconfig.DecodeContainerConfig(r.Body) 43 if err != nil && err != io.EOF { //Do not fail if body is empty. 44 return err 45 } 46 if c == nil { 47 c = &container.Config{} 48 } 49 50 if !s.daemon.Exists(cname) { 51 return derr.ErrorCodeNoSuchContainer.WithArgs(cname) 52 } 53 54 newConfig, err := dockerfile.BuildFromConfig(c, r.Form["changes"]) 55 if err != nil { 56 return err 57 } 58 59 commitCfg := &types.ContainerCommitConfig{ 60 Pause: pause, 61 Repo: r.Form.Get("repo"), 62 Tag: r.Form.Get("tag"), 63 Author: r.Form.Get("author"), 64 Comment: r.Form.Get("comment"), 65 Config: newConfig, 66 MergeConfigs: true, 67 } 68 69 imgID, err := s.daemon.Commit(cname, commitCfg) 70 if err != nil { 71 return err 72 } 73 74 return httputils.WriteJSON(w, http.StatusCreated, &types.ContainerCommitResponse{ 75 ID: string(imgID), 76 }) 77 } 78 79 // Creates an image from Pull or from Import 80 func (s *router) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 81 if err := httputils.ParseForm(r); err != nil { 82 return err 83 } 84 85 var ( 86 image = r.Form.Get("fromImage") 87 repo = r.Form.Get("repo") 88 tag = r.Form.Get("tag") 89 message = r.Form.Get("message") 90 ) 91 authEncoded := r.Header.Get("X-Registry-Auth") 92 authConfig := &types.AuthConfig{} 93 if authEncoded != "" { 94 authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) 95 if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil { 96 // for a pull it is not an error if no auth was given 97 // to increase compatibility with the existing api it is defaulting to be empty 98 authConfig = &types.AuthConfig{} 99 } 100 } 101 102 var ( 103 err error 104 output = ioutils.NewWriteFlusher(w) 105 ) 106 defer output.Close() 107 108 w.Header().Set("Content-Type", "application/json") 109 110 if image != "" { //pull 111 // Special case: "pull -a" may send an image name with a 112 // trailing :. This is ugly, but let's not break API 113 // compatibility. 114 image = strings.TrimSuffix(image, ":") 115 116 var ref reference.Named 117 ref, err = reference.ParseNamed(image) 118 if err == nil { 119 if tag != "" { 120 // The "tag" could actually be a digest. 121 var dgst digest.Digest 122 dgst, err = digest.ParseDigest(tag) 123 if err == nil { 124 ref, err = reference.WithDigest(ref, dgst) 125 } else { 126 ref, err = reference.WithTag(ref, tag) 127 } 128 } 129 if err == nil { 130 metaHeaders := map[string][]string{} 131 for k, v := range r.Header { 132 if strings.HasPrefix(k, "X-Meta-") { 133 metaHeaders[k] = v 134 } 135 } 136 137 err = s.daemon.PullImage(ref, metaHeaders, authConfig, output) 138 } 139 } 140 } else { //import 141 var newRef reference.Named 142 if repo != "" { 143 var err error 144 newRef, err = reference.ParseNamed(repo) 145 if err != nil { 146 return err 147 } 148 149 if _, isCanonical := newRef.(reference.Canonical); isCanonical { 150 return errors.New("cannot import digest reference") 151 } 152 153 if tag != "" { 154 newRef, err = reference.WithTag(newRef, tag) 155 if err != nil { 156 return err 157 } 158 } 159 } 160 161 src := r.Form.Get("fromSrc") 162 163 // 'err' MUST NOT be defined within this block, we need any error 164 // generated from the download to be available to the output 165 // stream processing below 166 var newConfig *container.Config 167 newConfig, err = dockerfile.BuildFromConfig(&container.Config{}, r.Form["changes"]) 168 if err != nil { 169 return err 170 } 171 172 err = s.daemon.ImportImage(src, newRef, message, r.Body, output, newConfig) 173 } 174 if err != nil { 175 if !output.Flushed() { 176 return err 177 } 178 sf := streamformatter.NewJSONStreamFormatter() 179 output.Write(sf.FormatError(err)) 180 } 181 182 return nil 183 } 184 185 func (s *router) postImagesPush(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 186 metaHeaders := map[string][]string{} 187 for k, v := range r.Header { 188 if strings.HasPrefix(k, "X-Meta-") { 189 metaHeaders[k] = v 190 } 191 } 192 if err := httputils.ParseForm(r); err != nil { 193 return err 194 } 195 authConfig := &types.AuthConfig{} 196 197 authEncoded := r.Header.Get("X-Registry-Auth") 198 if authEncoded != "" { 199 // the new format is to handle the authConfig as a header 200 authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) 201 if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil { 202 // to increase compatibility to existing api it is defaulting to be empty 203 authConfig = &types.AuthConfig{} 204 } 205 } else { 206 // the old format is supported for compatibility if there was no authConfig header 207 if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil { 208 return fmt.Errorf("Bad parameters and missing X-Registry-Auth: %v", err) 209 } 210 } 211 212 ref, err := reference.ParseNamed(vars["name"]) 213 if err != nil { 214 return err 215 } 216 tag := r.Form.Get("tag") 217 if tag != "" { 218 // Push by digest is not supported, so only tags are supported. 219 ref, err = reference.WithTag(ref, tag) 220 if err != nil { 221 return err 222 } 223 } 224 225 output := ioutils.NewWriteFlusher(w) 226 defer output.Close() 227 228 w.Header().Set("Content-Type", "application/json") 229 230 if err := s.daemon.PushImage(ref, metaHeaders, authConfig, output); err != nil { 231 if !output.Flushed() { 232 return err 233 } 234 sf := streamformatter.NewJSONStreamFormatter() 235 output.Write(sf.FormatError(err)) 236 } 237 return nil 238 } 239 240 func (s *router) getImagesGet(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 241 if err := httputils.ParseForm(r); err != nil { 242 return err 243 } 244 245 w.Header().Set("Content-Type", "application/x-tar") 246 247 output := ioutils.NewWriteFlusher(w) 248 defer output.Close() 249 var names []string 250 if name, ok := vars["name"]; ok { 251 names = []string{name} 252 } else { 253 names = r.Form["names"] 254 } 255 256 if err := s.daemon.ExportImage(names, output); err != nil { 257 if !output.Flushed() { 258 return err 259 } 260 sf := streamformatter.NewJSONStreamFormatter() 261 output.Write(sf.FormatError(err)) 262 } 263 return nil 264 } 265 266 func (s *router) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 267 return s.daemon.LoadImage(r.Body, w) 268 } 269 270 func (s *router) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 271 if err := httputils.ParseForm(r); err != nil { 272 return err 273 } 274 275 name := vars["name"] 276 277 if strings.TrimSpace(name) == "" { 278 return fmt.Errorf("image name cannot be blank") 279 } 280 281 force := httputils.BoolValue(r, "force") 282 prune := !httputils.BoolValue(r, "noprune") 283 284 list, err := s.daemon.ImageDelete(name, force, prune) 285 if err != nil { 286 return err 287 } 288 289 return httputils.WriteJSON(w, http.StatusOK, list) 290 } 291 292 func (s *router) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 293 imageInspect, err := s.daemon.LookupImage(vars["name"]) 294 if err != nil { 295 return err 296 } 297 298 return httputils.WriteJSON(w, http.StatusOK, imageInspect) 299 } 300 301 func (s *router) getImagesJSON(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 302 if err := httputils.ParseForm(r); err != nil { 303 return err 304 } 305 306 // FIXME: The filter parameter could just be a match filter 307 images, err := s.daemon.Images(r.Form.Get("filters"), r.Form.Get("filter"), httputils.BoolValue(r, "all")) 308 if err != nil { 309 return err 310 } 311 312 return httputils.WriteJSON(w, http.StatusOK, images) 313 } 314 315 func (s *router) getImagesHistory(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 316 name := vars["name"] 317 history, err := s.daemon.ImageHistory(name) 318 if err != nil { 319 return err 320 } 321 322 return httputils.WriteJSON(w, http.StatusOK, history) 323 } 324 325 func (s *router) postImagesTag(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 326 if err := httputils.ParseForm(r); err != nil { 327 return err 328 } 329 repo := r.Form.Get("repo") 330 tag := r.Form.Get("tag") 331 newTag, err := reference.WithName(repo) 332 if err != nil { 333 return err 334 } 335 if tag != "" { 336 if newTag, err = reference.WithTag(newTag, tag); err != nil { 337 return err 338 } 339 } 340 if err := s.daemon.TagImage(newTag, vars["name"]); err != nil { 341 return err 342 } 343 w.WriteHeader(http.StatusCreated) 344 return nil 345 } 346 347 func (s *router) getImagesSearch(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 348 if err := httputils.ParseForm(r); err != nil { 349 return err 350 } 351 var ( 352 config *types.AuthConfig 353 authEncoded = r.Header.Get("X-Registry-Auth") 354 headers = map[string][]string{} 355 ) 356 357 if authEncoded != "" { 358 authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) 359 if err := json.NewDecoder(authJSON).Decode(&config); err != nil { 360 // for a search it is not an error if no auth was given 361 // to increase compatibility with the existing api it is defaulting to be empty 362 config = &types.AuthConfig{} 363 } 364 } 365 for k, v := range r.Header { 366 if strings.HasPrefix(k, "X-Meta-") { 367 headers[k] = v 368 } 369 } 370 query, err := s.daemon.SearchRegistryForImages(r.Form.Get("term"), config, headers) 371 if err != nil { 372 return err 373 } 374 return httputils.WriteJSON(w, http.StatusOK, query.Results) 375 }