github.com/rsampaio/docker@v0.7.2-0.20150827203920-fdc73cc3fc31/api/server/image.go (about) 1 package server 2 3 import ( 4 "encoding/base64" 5 "encoding/json" 6 "fmt" 7 "io" 8 "net/http" 9 "strings" 10 11 "github.com/Sirupsen/logrus" 12 "github.com/docker/docker/api/types" 13 "github.com/docker/docker/builder" 14 "github.com/docker/docker/cliconfig" 15 "github.com/docker/docker/graph" 16 "github.com/docker/docker/pkg/ioutils" 17 "github.com/docker/docker/pkg/parsers" 18 "github.com/docker/docker/pkg/streamformatter" 19 "github.com/docker/docker/pkg/ulimit" 20 "github.com/docker/docker/pkg/version" 21 "github.com/docker/docker/runconfig" 22 "github.com/docker/docker/utils" 23 ) 24 25 func (s *Server) postCommit(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 26 if err := parseForm(r); err != nil { 27 return err 28 } 29 30 if err := checkForJSON(r); err != nil { 31 return err 32 } 33 34 cname := r.Form.Get("container") 35 36 pause := boolValue(r, "pause") 37 if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") { 38 pause = true 39 } 40 41 c, _, err := runconfig.DecodeContainerConfig(r.Body) 42 if err != nil && err != io.EOF { //Do not fail if body is empty. 43 return err 44 } 45 46 commitCfg := &builder.CommitConfig{ 47 Pause: pause, 48 Repo: r.Form.Get("repo"), 49 Tag: r.Form.Get("tag"), 50 Author: r.Form.Get("author"), 51 Comment: r.Form.Get("comment"), 52 Changes: r.Form["changes"], 53 Config: c, 54 } 55 56 imgID, err := builder.Commit(cname, s.daemon, commitCfg) 57 if err != nil { 58 return err 59 } 60 61 return writeJSON(w, http.StatusCreated, &types.ContainerCommitResponse{ 62 ID: imgID, 63 }) 64 } 65 66 // Creates an image from Pull or from Import 67 func (s *Server) postImagesCreate(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 68 if err := parseForm(r); err != nil { 69 return err 70 } 71 72 var ( 73 image = r.Form.Get("fromImage") 74 repo = r.Form.Get("repo") 75 tag = r.Form.Get("tag") 76 message = r.Form.Get("message") 77 ) 78 authEncoded := r.Header.Get("X-Registry-Auth") 79 authConfig := &cliconfig.AuthConfig{} 80 if authEncoded != "" { 81 authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) 82 if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil { 83 // for a pull it is not an error if no auth was given 84 // to increase compatibility with the existing api it is defaulting to be empty 85 authConfig = &cliconfig.AuthConfig{} 86 } 87 } 88 89 var ( 90 err error 91 output = ioutils.NewWriteFlusher(w) 92 ) 93 94 w.Header().Set("Content-Type", "application/json") 95 96 if image != "" { //pull 97 if tag == "" { 98 image, tag = parsers.ParseRepositoryTag(image) 99 } 100 metaHeaders := map[string][]string{} 101 for k, v := range r.Header { 102 if strings.HasPrefix(k, "X-Meta-") { 103 metaHeaders[k] = v 104 } 105 } 106 107 imagePullConfig := &graph.ImagePullConfig{ 108 MetaHeaders: metaHeaders, 109 AuthConfig: authConfig, 110 OutStream: output, 111 } 112 113 err = s.daemon.Repositories().Pull(image, tag, imagePullConfig) 114 } else { //import 115 if tag == "" { 116 repo, tag = parsers.ParseRepositoryTag(repo) 117 } 118 119 src := r.Form.Get("fromSrc") 120 121 // 'err' MUST NOT be defined within this block, we need any error 122 // generated from the download to be available to the output 123 // stream processing below 124 var newConfig *runconfig.Config 125 newConfig, err = builder.BuildFromConfig(s.daemon, &runconfig.Config{}, r.Form["changes"]) 126 if err != nil { 127 return err 128 } 129 130 err = s.daemon.Repositories().Import(src, repo, tag, message, r.Body, output, newConfig) 131 } 132 if err != nil { 133 if !output.Flushed() { 134 return err 135 } 136 sf := streamformatter.NewJSONStreamFormatter() 137 output.Write(sf.FormatError(err)) 138 } 139 140 return nil 141 } 142 143 func (s *Server) postImagesPush(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 144 if vars == nil { 145 return fmt.Errorf("Missing parameter") 146 } 147 148 metaHeaders := map[string][]string{} 149 for k, v := range r.Header { 150 if strings.HasPrefix(k, "X-Meta-") { 151 metaHeaders[k] = v 152 } 153 } 154 if err := parseForm(r); err != nil { 155 return err 156 } 157 authConfig := &cliconfig.AuthConfig{} 158 159 authEncoded := r.Header.Get("X-Registry-Auth") 160 if authEncoded != "" { 161 // the new format is to handle the authConfig as a header 162 authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) 163 if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil { 164 // to increase compatibility to existing api it is defaulting to be empty 165 authConfig = &cliconfig.AuthConfig{} 166 } 167 } else { 168 // the old format is supported for compatibility if there was no authConfig header 169 if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil { 170 return fmt.Errorf("Bad parameters and missing X-Registry-Auth: %v", err) 171 } 172 } 173 174 name := vars["name"] 175 output := ioutils.NewWriteFlusher(w) 176 imagePushConfig := &graph.ImagePushConfig{ 177 MetaHeaders: metaHeaders, 178 AuthConfig: authConfig, 179 Tag: r.Form.Get("tag"), 180 OutStream: output, 181 } 182 183 w.Header().Set("Content-Type", "application/json") 184 185 if err := s.daemon.Repositories().Push(name, imagePushConfig); err != nil { 186 if !output.Flushed() { 187 return err 188 } 189 sf := streamformatter.NewJSONStreamFormatter() 190 output.Write(sf.FormatError(err)) 191 } 192 return nil 193 } 194 195 func (s *Server) getImagesGet(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 196 if vars == nil { 197 return fmt.Errorf("Missing parameter") 198 } 199 if err := parseForm(r); err != nil { 200 return err 201 } 202 203 w.Header().Set("Content-Type", "application/x-tar") 204 205 output := ioutils.NewWriteFlusher(w) 206 var names []string 207 if name, ok := vars["name"]; ok { 208 names = []string{name} 209 } else { 210 names = r.Form["names"] 211 } 212 213 if err := s.daemon.Repositories().ImageExport(names, output); err != nil { 214 if !output.Flushed() { 215 return err 216 } 217 sf := streamformatter.NewJSONStreamFormatter() 218 output.Write(sf.FormatError(err)) 219 } 220 return nil 221 } 222 223 func (s *Server) postImagesLoad(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 224 return s.daemon.Repositories().Load(r.Body, w) 225 } 226 227 func (s *Server) deleteImages(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 228 if err := parseForm(r); err != nil { 229 return err 230 } 231 if vars == nil { 232 return fmt.Errorf("Missing parameter") 233 } 234 235 name := vars["name"] 236 force := boolValue(r, "force") 237 noprune := boolValue(r, "noprune") 238 239 list, err := s.daemon.ImageDelete(name, force, noprune) 240 if err != nil { 241 return err 242 } 243 244 return writeJSON(w, http.StatusOK, list) 245 } 246 247 func (s *Server) getImagesByName(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 248 if vars == nil { 249 return fmt.Errorf("Missing parameter") 250 } 251 252 imageInspect, err := s.daemon.Repositories().Lookup(vars["name"]) 253 if err != nil { 254 return err 255 } 256 257 return writeJSON(w, http.StatusOK, imageInspect) 258 } 259 260 func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 261 var ( 262 authConfigs = map[string]cliconfig.AuthConfig{} 263 authConfigsEncoded = r.Header.Get("X-Registry-Config") 264 buildConfig = builder.NewBuildConfig() 265 ) 266 267 if authConfigsEncoded != "" { 268 authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded)) 269 if err := json.NewDecoder(authConfigsJSON).Decode(&authConfigs); err != nil { 270 // for a pull it is not an error if no auth was given 271 // to increase compatibility with the existing api it is defaulting 272 // to be empty. 273 } 274 } 275 276 w.Header().Set("Content-Type", "application/json") 277 278 if boolValue(r, "forcerm") && version.GreaterThanOrEqualTo("1.12") { 279 buildConfig.Remove = true 280 } else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") { 281 buildConfig.Remove = true 282 } else { 283 buildConfig.Remove = boolValue(r, "rm") 284 } 285 if boolValue(r, "pull") && version.GreaterThanOrEqualTo("1.16") { 286 buildConfig.Pull = true 287 } 288 289 output := ioutils.NewWriteFlusher(w) 290 buildConfig.Stdout = output 291 buildConfig.Context = r.Body 292 293 buildConfig.RemoteURL = r.FormValue("remote") 294 buildConfig.DockerfileName = r.FormValue("dockerfile") 295 buildConfig.RepoName = r.FormValue("t") 296 buildConfig.SuppressOutput = boolValue(r, "q") 297 buildConfig.NoCache = boolValue(r, "nocache") 298 buildConfig.ForceRemove = boolValue(r, "forcerm") 299 buildConfig.AuthConfigs = authConfigs 300 buildConfig.MemorySwap = int64ValueOrZero(r, "memswap") 301 buildConfig.Memory = int64ValueOrZero(r, "memory") 302 buildConfig.CPUShares = int64ValueOrZero(r, "cpushares") 303 buildConfig.CPUPeriod = int64ValueOrZero(r, "cpuperiod") 304 buildConfig.CPUQuota = int64ValueOrZero(r, "cpuquota") 305 buildConfig.CPUSetCpus = r.FormValue("cpusetcpus") 306 buildConfig.CPUSetMems = r.FormValue("cpusetmems") 307 buildConfig.CgroupParent = r.FormValue("cgroupparent") 308 309 var buildUlimits = []*ulimit.Ulimit{} 310 ulimitsJSON := r.FormValue("ulimits") 311 if ulimitsJSON != "" { 312 if err := json.NewDecoder(strings.NewReader(ulimitsJSON)).Decode(&buildUlimits); err != nil { 313 return err 314 } 315 buildConfig.Ulimits = buildUlimits 316 } 317 318 // Job cancellation. Note: not all job types support this. 319 if closeNotifier, ok := w.(http.CloseNotifier); ok { 320 finished := make(chan struct{}) 321 defer close(finished) 322 go func() { 323 select { 324 case <-finished: 325 case <-closeNotifier.CloseNotify(): 326 logrus.Infof("Client disconnected, cancelling job: build") 327 buildConfig.Cancel() 328 } 329 }() 330 } 331 332 if err := builder.Build(s.daemon, buildConfig); err != nil { 333 // Do not write the error in the http output if it's still empty. 334 // This prevents from writing a 200(OK) when there is an interal error. 335 if !output.Flushed() { 336 return err 337 } 338 sf := streamformatter.NewJSONStreamFormatter() 339 w.Write(sf.FormatError(err)) 340 } 341 return nil 342 } 343 344 func (s *Server) getImagesJSON(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 345 if err := parseForm(r); err != nil { 346 return err 347 } 348 349 // FIXME: The filter parameter could just be a match filter 350 images, err := s.daemon.Repositories().Images(r.Form.Get("filters"), r.Form.Get("filter"), boolValue(r, "all")) 351 if err != nil { 352 return err 353 } 354 355 return writeJSON(w, http.StatusOK, images) 356 } 357 358 func (s *Server) getImagesHistory(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 359 if vars == nil { 360 return fmt.Errorf("Missing parameter") 361 } 362 363 name := vars["name"] 364 history, err := s.daemon.Repositories().History(name) 365 if err != nil { 366 return err 367 } 368 369 return writeJSON(w, http.StatusOK, history) 370 } 371 372 func (s *Server) postImagesTag(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 373 if err := parseForm(r); err != nil { 374 return err 375 } 376 if vars == nil { 377 return fmt.Errorf("Missing parameter") 378 } 379 380 repo := r.Form.Get("repo") 381 tag := r.Form.Get("tag") 382 force := boolValue(r, "force") 383 name := vars["name"] 384 if err := s.daemon.Repositories().Tag(repo, tag, name, force); err != nil { 385 return err 386 } 387 s.daemon.EventsService.Log("tag", utils.ImageReference(repo, tag), "") 388 w.WriteHeader(http.StatusCreated) 389 return nil 390 } 391 392 func (s *Server) getImagesSearch(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 393 if err := parseForm(r); err != nil { 394 return err 395 } 396 var ( 397 config *cliconfig.AuthConfig 398 authEncoded = r.Header.Get("X-Registry-Auth") 399 headers = map[string][]string{} 400 ) 401 402 if authEncoded != "" { 403 authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) 404 if err := json.NewDecoder(authJSON).Decode(&config); err != nil { 405 // for a search it is not an error if no auth was given 406 // to increase compatibility with the existing api it is defaulting to be empty 407 config = &cliconfig.AuthConfig{} 408 } 409 } 410 for k, v := range r.Header { 411 if strings.HasPrefix(k, "X-Meta-") { 412 headers[k] = v 413 } 414 } 415 query, err := s.daemon.RegistryService.Search(r.Form.Get("term"), config, headers) 416 if err != nil { 417 return err 418 } 419 return writeJSON(w, http.StatusOK, query.Results) 420 }