func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var ( authConfig = &cliconfig.AuthConfig{} configFileEncoded = r.Header.Get("X-Registry-Config") configFile = &cliconfig.ConfigFile{} buildConfig = builder.NewBuildConfig() ) if configFileEncoded != "" { configFileJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(configFileEncoded)) if err := json.NewDecoder(configFileJson).Decode(configFile); err != nil { // for a pull it is not an error if no auth was given // to increase compatibility with the existing api it is defaulting to be empty configFile = &cliconfig.ConfigFile{} } } w.Header().Set("Content-Type", "application/json") if boolValue(r, "forcerm") && version.GreaterThanOrEqualTo("1.12") { buildConfig.Remove = true } else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") { buildConfig.Remove = true } else { buildConfig.Remove = boolValue(r, "rm") } if boolValue(r, "pull") && version.GreaterThanOrEqualTo("1.16") { buildConfig.Pull = true } output := ioutils.NewWriteFlusher(w) buildConfig.Stdout = output buildConfig.Context = r.Body buildConfig.RemoteURL = r.FormValue("remote") buildConfig.DockerfileName = r.FormValue("dockerfile") buildConfig.RepoName = r.FormValue("t") buildConfig.SuppressOutput = boolValue(r, "q") buildConfig.NoCache = boolValue(r, "nocache") buildConfig.ForceRemove = boolValue(r, "forcerm") buildConfig.AuthConfig = authConfig buildConfig.ConfigFile = configFile buildConfig.MemorySwap = int64ValueOrZero(r, "memswap") buildConfig.Memory = int64ValueOrZero(r, "memory") buildConfig.CpuShares = int64ValueOrZero(r, "cpushares") buildConfig.CpuPeriod = int64ValueOrZero(r, "cpuperiod") buildConfig.CpuQuota = int64ValueOrZero(r, "cpuquota") buildConfig.CpuSetCpus = r.FormValue("cpusetcpus") buildConfig.CpuSetMems = r.FormValue("cpusetmems") buildConfig.CgroupParent = r.FormValue("cgroupparent") // Job cancellation. Note: not all job types support this. if closeNotifier, ok := w.(http.CloseNotifier); ok { finished := make(chan struct{}) defer close(finished) go func() { select { case <-finished: case <-closeNotifier.CloseNotify(): logrus.Infof("Client disconnected, cancelling job: build") buildConfig.Cancel() } }() } if err := builder.Build(s.daemon, buildConfig); err != nil { // Do not write the error in the http output if it's still empty. // This prevents from writing a 200(OK) when there is an interal error. if !output.Flushed() { return err } sf := streamformatter.NewJSONStreamFormatter() w.Write(sf.FormatError(err)) } return nil }
func (s *Server) postBuild(version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error { if version.LessThan("1.3") { return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.") } var ( authEncoded = r.Header.Get("X-Registry-Auth") authConfig = &cliconfig.AuthConfig{} configFileEncoded = r.Header.Get("X-Registry-Config") configFile = &cliconfig.ConfigFile{} buildConfig = builder.NewBuildConfig() ) // This block can be removed when API versions prior to 1.9 are deprecated. // Both headers will be parsed and sent along to the daemon, but if a non-empty // ConfigFile is present, any value provided as an AuthConfig directly will // be overridden. See BuildFile::CmdFrom for details. if version.LessThan("1.9") && authEncoded != "" { authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) if err := json.NewDecoder(authJson).Decode(authConfig); err != nil { // for a pull it is not an error if no auth was given // to increase compatibility with the existing api it is defaulting to be empty authConfig = &cliconfig.AuthConfig{} } } if configFileEncoded != "" { configFileJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(configFileEncoded)) if err := json.NewDecoder(configFileJson).Decode(configFile); err != nil { // for a pull it is not an error if no auth was given // to increase compatibility with the existing api it is defaulting to be empty configFile = &cliconfig.ConfigFile{} } } if version.GreaterThanOrEqualTo("1.8") { w.Header().Set("Content-Type", "application/json") buildConfig.JSONFormat = true } if boolValue(r, "forcerm") && version.GreaterThanOrEqualTo("1.12") { buildConfig.Remove = true } else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") { buildConfig.Remove = true } else { buildConfig.Remove = boolValue(r, "rm") } if boolValue(r, "pull") && version.GreaterThanOrEqualTo("1.16") { buildConfig.Pull = true } output := utils.NewWriteFlusher(w) buildConfig.Stdout = output buildConfig.Context = r.Body buildConfig.RemoteURL = r.FormValue("remote") buildConfig.DockerfileName = r.FormValue("dockerfile") buildConfig.RepoName = r.FormValue("t") buildConfig.SuppressOutput = boolValue(r, "q") buildConfig.NoCache = boolValue(r, "nocache") buildConfig.ForceRemove = boolValue(r, "forcerm") buildConfig.AuthConfig = authConfig buildConfig.ConfigFile = configFile buildConfig.MemorySwap = int64ValueOrZero(r, "memswap") buildConfig.Memory = int64ValueOrZero(r, "memory") buildConfig.CpuShares = int64ValueOrZero(r, "cpushares") buildConfig.CpuQuota = int64ValueOrZero(r, "cpuquota") buildConfig.CpuSetCpus = r.FormValue("cpusetcpus") buildConfig.CpuSetMems = r.FormValue("cpusetmems") buildConfig.CgroupParent = r.FormValue("cgroupparent") // Job cancellation. Note: not all job types support this. if closeNotifier, ok := w.(http.CloseNotifier); ok { finished := make(chan struct{}) defer close(finished) go func() { select { case <-finished: case <-closeNotifier.CloseNotify(): logrus.Infof("Client disconnected, cancelling job: build") buildConfig.Cancel() } }() } if err := builder.Build(s.daemon, buildConfig); err != nil { // Do not write the error in the http output if it's still empty. // This prevents from writing a 200(OK) when there is an interal error. if !output.Flushed() { return err } sf := streamformatter.NewStreamFormatter(version.GreaterThanOrEqualTo("1.8")) w.Write(sf.FormatError(err)) } return nil }