func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var ( authConfigs = map[string]types.AuthConfig{} authConfigsEncoded = r.Header.Get("X-Registry-Config") buildConfig = &dockerfile.Config{} notVerboseBuffer = bytes.NewBuffer(nil) ) if authConfigsEncoded != "" { authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded)) if err := json.NewDecoder(authConfigsJSON).Decode(&authConfigs); 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. } } w.Header().Set("Content-Type", "application/json") version := httputils.VersionFromContext(ctx) output := ioutils.NewWriteFlusher(w) defer output.Close() sf := streamformatter.NewJSONStreamFormatter() errf := func(err error) error { if !buildConfig.Verbose && notVerboseBuffer.Len() > 0 { output.Write(notVerboseBuffer.Bytes()) } // 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 internal error. if !output.Flushed() { return err } _, err = w.Write(sf.FormatError(errors.New(utils.GetErrorMessage(err)))) if err != nil { logrus.Warnf("could not write error response: %v", err) } return nil } if httputils.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 = httputils.BoolValue(r, "rm") } if httputils.BoolValue(r, "pull") && version.GreaterThanOrEqualTo("1.16") { buildConfig.Pull = true } repoAndTags, err := sanitizeRepoAndTags(r.Form["t"]) if err != nil { return errf(err) } buildConfig.DockerfileName = r.FormValue("dockerfile") buildConfig.Verbose = !httputils.BoolValue(r, "q") buildConfig.UseCache = !httputils.BoolValue(r, "nocache") buildConfig.ForceRemove = httputils.BoolValue(r, "forcerm") buildConfig.MemorySwap = httputils.Int64ValueOrZero(r, "memswap") buildConfig.Memory = httputils.Int64ValueOrZero(r, "memory") buildConfig.CPUShares = httputils.Int64ValueOrZero(r, "cpushares") buildConfig.CPUPeriod = httputils.Int64ValueOrZero(r, "cpuperiod") buildConfig.CPUQuota = httputils.Int64ValueOrZero(r, "cpuquota") buildConfig.CPUSetCpus = r.FormValue("cpusetcpus") buildConfig.CPUSetMems = r.FormValue("cpusetmems") buildConfig.CgroupParent = r.FormValue("cgroupparent") if r.Form.Get("shmsize") != "" { shmSize, err := strconv.ParseInt(r.Form.Get("shmsize"), 10, 64) if err != nil { return errf(err) } buildConfig.ShmSize = &shmSize } if i := container.IsolationLevel(r.FormValue("isolation")); i != "" { if !container.IsolationLevel.IsValid(i) { return errf(fmt.Errorf("Unsupported isolation: %q", i)) } buildConfig.Isolation = i } var buildUlimits = []*ulimit.Ulimit{} ulimitsJSON := r.FormValue("ulimits") if ulimitsJSON != "" { if err := json.NewDecoder(strings.NewReader(ulimitsJSON)).Decode(&buildUlimits); err != nil { return errf(err) } buildConfig.Ulimits = buildUlimits } var buildArgs = map[string]string{} buildArgsJSON := r.FormValue("buildargs") if buildArgsJSON != "" { if err := json.NewDecoder(strings.NewReader(buildArgsJSON)).Decode(&buildArgs); err != nil { return errf(err) } buildConfig.BuildArgs = buildArgs } remoteURL := r.FormValue("remote") // Currently, only used if context is from a remote url. // Look at code in DetectContextFromRemoteURL for more information. createProgressReader := func(in io.ReadCloser) io.ReadCloser { progressOutput := sf.NewProgressOutput(output, true) if !buildConfig.Verbose { progressOutput = sf.NewProgressOutput(notVerboseBuffer, true) } return progress.NewProgressReader(in, progressOutput, r.ContentLength, "Downloading context", remoteURL) } var ( context builder.ModifiableContext dockerfileName string ) context, dockerfileName, err = daemonbuilder.DetectContextFromRemoteURL(r.Body, remoteURL, createProgressReader) if err != nil { return errf(err) } defer func() { if err := context.Close(); err != nil { logrus.Debugf("[BUILDER] failed to remove temporary context: %v", err) } }() uidMaps, gidMaps := br.backend.GetUIDGIDMaps() defaultArchiver := &archive.Archiver{ Untar: chrootarchive.Untar, UIDMaps: uidMaps, GIDMaps: gidMaps, } docker := &daemonbuilder.Docker{ Daemon: br.backend, OutOld: output, AuthConfigs: authConfigs, Archiver: defaultArchiver, } if !buildConfig.Verbose { docker.OutOld = notVerboseBuffer } b, err := dockerfile.NewBuilder(buildConfig, docker, builder.DockerIgnoreContext{ModifiableContext: context}, nil) if err != nil { return errf(err) } b.Stdout = &streamformatter.StdoutFormatter{Writer: output, StreamFormatter: sf} b.Stderr = &streamformatter.StderrFormatter{Writer: output, StreamFormatter: sf} if !buildConfig.Verbose { b.Stdout = &streamformatter.StdoutFormatter{Writer: notVerboseBuffer, StreamFormatter: sf} b.Stderr = &streamformatter.StderrFormatter{Writer: notVerboseBuffer, StreamFormatter: sf} } 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") b.Cancel() } }() } if len(dockerfileName) > 0 { b.DockerfileName = dockerfileName } imgID, err := b.Build() if err != nil { return errf(err) } for _, rt := range repoAndTags { if err := br.backend.TagImage(rt, imgID); err != nil { return errf(err) } } // Everything worked so if -q was provided the output from the daemon // should be just the image ID and we'll print that to stdout. if !buildConfig.Verbose { stdout := &streamformatter.StdoutFormatter{Writer: output, StreamFormatter: sf} fmt.Fprintf(stdout, "%s\n", string(imgID)) } return nil }
func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBuildOptions, error) { version := httputils.VersionFromContext(ctx) options := &types.ImageBuildOptions{} if httputils.BoolValue(r, "forcerm") && version.GreaterThanOrEqualTo("1.12") { options.Remove = true } else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") { options.Remove = true } else { options.Remove = httputils.BoolValue(r, "rm") } if httputils.BoolValue(r, "pull") && version.GreaterThanOrEqualTo("1.16") { options.PullParent = true } options.Dockerfile = r.FormValue("dockerfile") options.SuppressOutput = httputils.BoolValue(r, "q") options.NoCache = httputils.BoolValue(r, "nocache") options.ForceRemove = httputils.BoolValue(r, "forcerm") options.MemorySwap = httputils.Int64ValueOrZero(r, "memswap") options.Memory = httputils.Int64ValueOrZero(r, "memory") options.CPUShares = httputils.Int64ValueOrZero(r, "cpushares") options.CPUPeriod = httputils.Int64ValueOrZero(r, "cpuperiod") options.CPUQuota = httputils.Int64ValueOrZero(r, "cpuquota") options.CPUSetCPUs = r.FormValue("cpusetcpus") options.CPUSetMems = r.FormValue("cpusetmems") options.CgroupParent = r.FormValue("cgroupparent") options.Tags = r.Form["t"] if r.Form.Get("shmsize") != "" { shmSize, err := strconv.ParseInt(r.Form.Get("shmsize"), 10, 64) if err != nil { return nil, err } options.ShmSize = shmSize } if i := container.Isolation(r.FormValue("isolation")); i != "" { if !container.Isolation.IsValid(i) { return nil, fmt.Errorf("Unsupported isolation: %q", i) } options.Isolation = i } var buildUlimits = []*units.Ulimit{} ulimitsJSON := r.FormValue("ulimits") if ulimitsJSON != "" { if err := json.NewDecoder(strings.NewReader(ulimitsJSON)).Decode(&buildUlimits); err != nil { return nil, err } options.Ulimits = buildUlimits } var buildArgs = map[string]string{} buildArgsJSON := r.FormValue("buildargs") if buildArgsJSON != "" { if err := json.NewDecoder(strings.NewReader(buildArgsJSON)).Decode(&buildArgs); err != nil { return nil, err } options.BuildArgs = buildArgs } var labels = map[string]string{} labelsJSON := r.FormValue("labels") if labelsJSON != "" { if err := json.NewDecoder(strings.NewReader(labelsJSON)).Decode(&labels); err != nil { return nil, err } options.Labels = labels } return options, nil }
func (s *router) postBuild(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var ( authConfigs = map[string]cliconfig.AuthConfig{} authConfigsEncoded = r.Header.Get("X-Registry-Config") buildConfig = &dockerfile.Config{} ) if authConfigsEncoded != "" { authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded)) if err := json.NewDecoder(authConfigsJSON).Decode(&authConfigs); 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. } } w.Header().Set("Content-Type", "application/json") version := httputils.VersionFromContext(ctx) output := ioutils.NewWriteFlusher(w) sf := streamformatter.NewJSONStreamFormatter() errf := func(err error) error { // 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 } _, err = w.Write(sf.FormatError(errors.New(utils.GetErrorMessage(err)))) if err != nil { logrus.Warnf("could not write error response: %v", err) } return nil } if httputils.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 = httputils.BoolValue(r, "rm") } if httputils.BoolValue(r, "pull") && version.GreaterThanOrEqualTo("1.16") { buildConfig.Pull = true } repoName, tag := parsers.ParseRepositoryTag(r.FormValue("t")) if repoName != "" { if err := registry.ValidateRepositoryName(repoName); err != nil { return errf(err) } if len(tag) > 0 { if err := tags.ValidateTagName(tag); err != nil { return errf(err) } } } buildConfig.DockerfileName = r.FormValue("dockerfile") buildConfig.Verbose = !httputils.BoolValue(r, "q") buildConfig.UseCache = !httputils.BoolValue(r, "nocache") buildConfig.ForceRemove = httputils.BoolValue(r, "forcerm") buildConfig.MemorySwap = httputils.Int64ValueOrZero(r, "memswap") buildConfig.Memory = httputils.Int64ValueOrZero(r, "memory") buildConfig.CPUShares = httputils.Int64ValueOrZero(r, "cpushares") buildConfig.CPUPeriod = httputils.Int64ValueOrZero(r, "cpuperiod") buildConfig.CPUQuota = httputils.Int64ValueOrZero(r, "cpuquota") buildConfig.CPUSetCpus = r.FormValue("cpusetcpus") buildConfig.CPUSetMems = r.FormValue("cpusetmems") buildConfig.CgroupParent = r.FormValue("cgroupparent") var buildUlimits = []*ulimit.Ulimit{} ulimitsJSON := r.FormValue("ulimits") if ulimitsJSON != "" { if err := json.NewDecoder(strings.NewReader(ulimitsJSON)).Decode(&buildUlimits); err != nil { return errf(err) } buildConfig.Ulimits = buildUlimits } var buildArgs = map[string]string{} buildArgsJSON := r.FormValue("buildargs") if buildArgsJSON != "" { if err := json.NewDecoder(strings.NewReader(buildArgsJSON)).Decode(&buildArgs); err != nil { return errf(err) } buildConfig.BuildArgs = buildArgs } remoteURL := r.FormValue("remote") // Currently, only used if context is from a remote url. // The field `In` is set by DetectContextFromRemoteURL. // Look at code in DetectContextFromRemoteURL for more information. pReader := &progressreader.Config{ // TODO: make progressreader streamformatter-agnostic Out: output, Formatter: sf, Size: r.ContentLength, NewLines: true, ID: "Downloading context", Action: remoteURL, } var ( context builder.ModifiableContext dockerfileName string err error ) context, dockerfileName, err = daemonbuilder.DetectContextFromRemoteURL(r.Body, remoteURL, pReader) if err != nil { return errf(err) } defer func() { if err := context.Close(); err != nil { logrus.Debugf("[BUILDER] failed to remove temporary context: %v", err) } }() uidMaps, gidMaps := s.daemon.GetUIDGIDMaps() defaultArchiver := &archive.Archiver{ Untar: chrootarchive.Untar, UIDMaps: uidMaps, GIDMaps: gidMaps, } docker := daemonbuilder.Docker{s.daemon, output, authConfigs, defaultArchiver} b, err := dockerfile.NewBuilder(buildConfig, docker, builder.DockerIgnoreContext{context}, nil) if err != nil { return errf(err) } b.Stdout = &streamformatter.StdoutFormatter{Writer: output, StreamFormatter: sf} b.Stderr = &streamformatter.StderrFormatter{Writer: output, StreamFormatter: sf} 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") b.Cancel() } }() } if len(dockerfileName) > 0 { b.DockerfileName = dockerfileName } imgID, err := b.Build() if err != nil { return errf(err) } if repoName != "" { if err := s.daemon.TagImage(repoName, tag, string(imgID), true); err != nil { return errf(err) } } return nil }
func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBuildOptions, error) { version := httputils.VersionFromContext(ctx) options := &types.ImageBuildOptions{} if httputils.BoolValue(r, "forcerm") && versions.GreaterThanOrEqualTo(version, "1.12") { options.Remove = true } else if r.FormValue("rm") == "" && versions.GreaterThanOrEqualTo(version, "1.12") { options.Remove = true } else { options.Remove = httputils.BoolValue(r, "rm") } if httputils.BoolValue(r, "pull") && versions.GreaterThanOrEqualTo(version, "1.16") { options.PullParent = true } options.Dockerfile = r.FormValue("dockerfile") options.SuppressOutput = httputils.BoolValue(r, "q") options.NoCache = httputils.BoolValue(r, "nocache") options.ForceRemove = httputils.BoolValue(r, "forcerm") options.MemorySwap = httputils.Int64ValueOrZero(r, "memswap") options.Memory = httputils.Int64ValueOrZero(r, "memory") options.CPUShares = httputils.Int64ValueOrZero(r, "cpushares") options.CPUPeriod = httputils.Int64ValueOrZero(r, "cpuperiod") options.CPUQuota = httputils.Int64ValueOrZero(r, "cpuquota") options.CPUSetCPUs = r.FormValue("cpusetcpus") options.CPUSetMems = r.FormValue("cpusetmems") options.CgroupParent = r.FormValue("cgroupparent") options.Tags = r.Form["t"] options.SecurityOpt = r.Form["securityopt"] if r.Form.Get("shmsize") != "" { shmSize, err := strconv.ParseInt(r.Form.Get("shmsize"), 10, 64) if err != nil { return nil, err } options.ShmSize = shmSize } if i := container.Isolation(r.FormValue("isolation")); i != "" { if !container.Isolation.IsValid(i) { return nil, fmt.Errorf("Unsupported isolation: %q", i) } options.Isolation = i } if runtime.GOOS != "windows" && options.SecurityOpt != nil { return nil, fmt.Errorf("the daemon on this platform does not support --security-opt to build") } var buildUlimits = []*units.Ulimit{} ulimitsJSON := r.FormValue("ulimits") if ulimitsJSON != "" { if err := json.Unmarshal([]byte(ulimitsJSON), &buildUlimits); err != nil { return nil, err } options.Ulimits = buildUlimits } var buildArgs = map[string]string{} buildArgsJSON := r.FormValue("buildargs") if buildArgsJSON != "" { if err := json.Unmarshal([]byte(buildArgsJSON), &buildArgs); err != nil { return nil, err } options.BuildArgs = buildArgs } var labels = map[string]string{} labelsJSON := r.FormValue("labels") if labelsJSON != "" { if err := json.Unmarshal([]byte(labelsJSON), &labels); err != nil { return nil, err } options.Labels = labels } var cacheFrom = []string{} cacheFromJSON := r.FormValue("cachefrom") if cacheFromJSON != "" { if err := json.Unmarshal([]byte(cacheFromJSON), &cacheFrom); err != nil { return nil, err } options.CacheFrom = cacheFrom } return options, nil }
func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBuildOptions, error) { version := httputils.VersionFromContext(ctx) options := &types.ImageBuildOptions{} if httputils.BoolValue(r, "forcerm") && versions.GreaterThanOrEqualTo(version, "1.12") { options.Remove = true } else if r.FormValue("rm") == "" && versions.GreaterThanOrEqualTo(version, "1.12") { options.Remove = true } else { options.Remove = httputils.BoolValue(r, "rm") } if httputils.BoolValue(r, "pull") && versions.GreaterThanOrEqualTo(version, "1.16") { options.PullParent = true } options.Dockerfile = r.FormValue("dockerfile") options.SuppressOutput = httputils.BoolValue(r, "q") options.NoCache = httputils.BoolValue(r, "nocache") options.ForceRemove = httputils.BoolValue(r, "forcerm") options.MemorySwap = httputils.Int64ValueOrZero(r, "memswap") options.Memory = httputils.Int64ValueOrZero(r, "memory") options.CPUShares = httputils.Int64ValueOrZero(r, "cpushares") options.CPUPeriod = httputils.Int64ValueOrZero(r, "cpuperiod") options.CPUQuota = httputils.Int64ValueOrZero(r, "cpuquota") options.CPUSetCPUs = r.FormValue("cpusetcpus") options.CPUSetMems = r.FormValue("cpusetmems") options.CgroupParent = r.FormValue("cgroupparent") options.NetworkMode = r.FormValue("networkmode") options.Tags = r.Form["t"] options.SecurityOpt = r.Form["securityopt"] options.Squash = httputils.BoolValue(r, "squash") if r.Form.Get("shmsize") != "" { shmSize, err := strconv.ParseInt(r.Form.Get("shmsize"), 10, 64) if err != nil { return nil, err } options.ShmSize = shmSize } if i := container.Isolation(r.FormValue("isolation")); i != "" { if !container.Isolation.IsValid(i) { return nil, fmt.Errorf("Unsupported isolation: %q", i) } options.Isolation = i } if runtime.GOOS != "windows" && options.SecurityOpt != nil { return nil, fmt.Errorf("the daemon on this platform does not support --security-opt to build") } var buildUlimits = []*units.Ulimit{} ulimitsJSON := r.FormValue("ulimits") if ulimitsJSON != "" { if err := json.Unmarshal([]byte(ulimitsJSON), &buildUlimits); err != nil { return nil, err } options.Ulimits = buildUlimits } var buildArgs = map[string]*string{} buildArgsJSON := r.FormValue("buildargs") // Note that there are two ways a --build-arg might appear in the // json of the query param: // "foo":"bar" // and "foo":nil // The first is the normal case, ie. --build-arg foo=bar // or --build-arg foo // where foo's value was picked up from an env var. // The second ("foo":nil) is where they put --build-arg foo // but "foo" isn't set as an env var. In that case we can't just drop // the fact they mentioned it, we need to pass that along to the builder // so that it can print a warning about "foo" being unused if there is // no "ARG foo" in the Dockerfile. if buildArgsJSON != "" { if err := json.Unmarshal([]byte(buildArgsJSON), &buildArgs); err != nil { return nil, err } options.BuildArgs = buildArgs } var labels = map[string]string{} labelsJSON := r.FormValue("labels") if labelsJSON != "" { if err := json.Unmarshal([]byte(labelsJSON), &labels); err != nil { return nil, err } options.Labels = labels } var cacheFrom = []string{} cacheFromJSON := r.FormValue("cachefrom") if cacheFromJSON != "" { if err := json.Unmarshal([]byte(cacheFromJSON), &cacheFrom); err != nil { return nil, err } options.CacheFrom = cacheFrom } return options, nil }
func (s *router) postBuild(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { var ( authConfigs = map[string]cliconfig.AuthConfig{} authConfigsEncoded = r.Header.Get("X-Registry-Config") buildConfig = builder.NewBuildConfig() ) if authConfigsEncoded != "" { authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded)) if err := json.NewDecoder(authConfigsJSON).Decode(&authConfigs); 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. } } w.Header().Set("Content-Type", "application/json") version := httputils.VersionFromContext(ctx) if httputils.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 = httputils.BoolValue(r, "rm") } if httputils.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 = httputils.BoolValue(r, "q") buildConfig.NoCache = httputils.BoolValue(r, "nocache") buildConfig.ForceRemove = httputils.BoolValue(r, "forcerm") buildConfig.AuthConfigs = authConfigs buildConfig.MemorySwap = httputils.Int64ValueOrZero(r, "memswap") buildConfig.Memory = httputils.Int64ValueOrZero(r, "memory") buildConfig.CPUShares = httputils.Int64ValueOrZero(r, "cpushares") buildConfig.CPUPeriod = httputils.Int64ValueOrZero(r, "cpuperiod") buildConfig.CPUQuota = httputils.Int64ValueOrZero(r, "cpuquota") buildConfig.CPUSetCpus = r.FormValue("cpusetcpus") buildConfig.CPUSetMems = r.FormValue("cpusetmems") buildConfig.CgroupParent = r.FormValue("cgroupparent") var buildUlimits = []*ulimit.Ulimit{} ulimitsJSON := r.FormValue("ulimits") if ulimitsJSON != "" { if err := json.NewDecoder(strings.NewReader(ulimitsJSON)).Decode(&buildUlimits); err != nil { return err } buildConfig.Ulimits = buildUlimits } var buildArgs = map[string]string{} buildArgsJSON := r.FormValue("buildargs") if buildArgsJSON != "" { if err := json.NewDecoder(strings.NewReader(buildArgsJSON)).Decode(&buildArgs); err != nil { return err } } buildConfig.BuildArgs = buildArgs // 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(errors.New(utils.GetErrorMessage(err)))) } return nil }