github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/api/server/router/build/build_routes.go (about) 1 package build 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "encoding/json" 7 "fmt" 8 "io" 9 "net/http" 10 "strconv" 11 "strings" 12 "sync" 13 14 "github.com/Sirupsen/logrus" 15 "github.com/docker/docker/api/server/httputils" 16 "github.com/docker/docker/api/types/backend" 17 "github.com/docker/docker/pkg/ioutils" 18 "github.com/docker/docker/pkg/progress" 19 "github.com/docker/docker/pkg/streamformatter" 20 "github.com/docker/engine-api/types" 21 "github.com/docker/engine-api/types/container" 22 "github.com/docker/go-units" 23 "golang.org/x/net/context" 24 ) 25 26 func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBuildOptions, error) { 27 version := httputils.VersionFromContext(ctx) 28 options := &types.ImageBuildOptions{} 29 if httputils.BoolValue(r, "forcerm") && version.GreaterThanOrEqualTo("1.12") { 30 options.Remove = true 31 } else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") { 32 options.Remove = true 33 } else { 34 options.Remove = httputils.BoolValue(r, "rm") 35 } 36 if httputils.BoolValue(r, "pull") && version.GreaterThanOrEqualTo("1.16") { 37 options.PullParent = true 38 } 39 40 options.Dockerfile = r.FormValue("dockerfile") 41 options.SuppressOutput = httputils.BoolValue(r, "q") 42 options.NoCache = httputils.BoolValue(r, "nocache") 43 options.ForceRemove = httputils.BoolValue(r, "forcerm") 44 options.MemorySwap = httputils.Int64ValueOrZero(r, "memswap") 45 options.Memory = httputils.Int64ValueOrZero(r, "memory") 46 options.CPUShares = httputils.Int64ValueOrZero(r, "cpushares") 47 options.CPUPeriod = httputils.Int64ValueOrZero(r, "cpuperiod") 48 options.CPUQuota = httputils.Int64ValueOrZero(r, "cpuquota") 49 options.CPUSetCPUs = r.FormValue("cpusetcpus") 50 options.CPUSetMems = r.FormValue("cpusetmems") 51 options.CgroupParent = r.FormValue("cgroupparent") 52 options.Tags = r.Form["t"] 53 54 if r.Form.Get("shmsize") != "" { 55 shmSize, err := strconv.ParseInt(r.Form.Get("shmsize"), 10, 64) 56 if err != nil { 57 return nil, err 58 } 59 options.ShmSize = shmSize 60 } 61 62 if i := container.Isolation(r.FormValue("isolation")); i != "" { 63 if !container.Isolation.IsValid(i) { 64 return nil, fmt.Errorf("Unsupported isolation: %q", i) 65 } 66 options.Isolation = i 67 } 68 69 var buildUlimits = []*units.Ulimit{} 70 ulimitsJSON := r.FormValue("ulimits") 71 if ulimitsJSON != "" { 72 if err := json.NewDecoder(strings.NewReader(ulimitsJSON)).Decode(&buildUlimits); err != nil { 73 return nil, err 74 } 75 options.Ulimits = buildUlimits 76 } 77 78 var buildArgs = map[string]string{} 79 buildArgsJSON := r.FormValue("buildargs") 80 if buildArgsJSON != "" { 81 if err := json.NewDecoder(strings.NewReader(buildArgsJSON)).Decode(&buildArgs); err != nil { 82 return nil, err 83 } 84 options.BuildArgs = buildArgs 85 } 86 var labels = map[string]string{} 87 labelsJSON := r.FormValue("labels") 88 if labelsJSON != "" { 89 if err := json.NewDecoder(strings.NewReader(labelsJSON)).Decode(&labels); err != nil { 90 return nil, err 91 } 92 options.Labels = labels 93 } 94 95 return options, nil 96 } 97 98 type syncWriter struct { 99 w io.Writer 100 mu sync.Mutex 101 } 102 103 func (s *syncWriter) Write(b []byte) (count int, err error) { 104 s.mu.Lock() 105 count, err = s.w.Write(b) 106 s.mu.Unlock() 107 return 108 } 109 110 func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 111 var ( 112 authConfigs = map[string]types.AuthConfig{} 113 authConfigsEncoded = r.Header.Get("X-Registry-Config") 114 notVerboseBuffer = bytes.NewBuffer(nil) 115 ) 116 117 if authConfigsEncoded != "" { 118 authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded)) 119 if err := json.NewDecoder(authConfigsJSON).Decode(&authConfigs); err != nil { 120 // for a pull it is not an error if no auth was given 121 // to increase compatibility with the existing api it is defaulting 122 // to be empty. 123 } 124 } 125 126 w.Header().Set("Content-Type", "application/json") 127 128 output := ioutils.NewWriteFlusher(w) 129 defer output.Close() 130 sf := streamformatter.NewJSONStreamFormatter() 131 errf := func(err error) error { 132 if httputils.BoolValue(r, "q") && notVerboseBuffer.Len() > 0 { 133 output.Write(notVerboseBuffer.Bytes()) 134 } 135 // Do not write the error in the http output if it's still empty. 136 // This prevents from writing a 200(OK) when there is an internal error. 137 if !output.Flushed() { 138 return err 139 } 140 _, err = w.Write(sf.FormatError(err)) 141 if err != nil { 142 logrus.Warnf("could not write error response: %v", err) 143 } 144 return nil 145 } 146 147 buildOptions, err := newImageBuildOptions(ctx, r) 148 if err != nil { 149 return errf(err) 150 } 151 buildOptions.AuthConfigs = authConfigs 152 153 remoteURL := r.FormValue("remote") 154 155 // Currently, only used if context is from a remote url. 156 // Look at code in DetectContextFromRemoteURL for more information. 157 createProgressReader := func(in io.ReadCloser) io.ReadCloser { 158 progressOutput := sf.NewProgressOutput(output, true) 159 if buildOptions.SuppressOutput { 160 progressOutput = sf.NewProgressOutput(notVerboseBuffer, true) 161 } 162 return progress.NewProgressReader(in, progressOutput, r.ContentLength, "Downloading context", remoteURL) 163 } 164 165 var out io.Writer = output 166 if buildOptions.SuppressOutput { 167 out = notVerboseBuffer 168 } 169 out = &syncWriter{w: out} 170 stdout := &streamformatter.StdoutFormatter{Writer: out, StreamFormatter: sf} 171 stderr := &streamformatter.StderrFormatter{Writer: out, StreamFormatter: sf} 172 173 pg := backend.ProgressWriter{ 174 Output: out, 175 StdoutFormatter: stdout, 176 StderrFormatter: stderr, 177 ProgressReaderFunc: createProgressReader, 178 } 179 180 imgID, err := br.backend.BuildFromContext(ctx, r.Body, remoteURL, buildOptions, pg) 181 if err != nil { 182 return errf(err) 183 } 184 185 // Everything worked so if -q was provided the output from the daemon 186 // should be just the image ID and we'll print that to stdout. 187 if buildOptions.SuppressOutput { 188 stdout := &streamformatter.StdoutFormatter{Writer: output, StreamFormatter: sf} 189 fmt.Fprintf(stdout, "%s\n", string(imgID)) 190 } 191 192 return nil 193 }