github.com/rita33cool1/iot-system-gateway@v0.0.0-20200911033302-e65bde238cc5/docker-engine/api/server/router/build/build_routes.go (about) 1 package build // import "github.com/docker/docker/api/server/router/build" 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "encoding/json" 7 "fmt" 8 "io" 9 "net/http" 10 "runtime" 11 "strconv" 12 "strings" 13 "sync" 14 15 "github.com/docker/docker/api/server/httputils" 16 "github.com/docker/docker/api/types" 17 "github.com/docker/docker/api/types/backend" 18 "github.com/docker/docker/api/types/container" 19 "github.com/docker/docker/api/types/versions" 20 "github.com/docker/docker/errdefs" 21 "github.com/docker/docker/pkg/ioutils" 22 "github.com/docker/docker/pkg/progress" 23 "github.com/docker/docker/pkg/streamformatter" 24 "github.com/docker/docker/pkg/system" 25 units "github.com/docker/go-units" 26 "github.com/pkg/errors" 27 "github.com/sirupsen/logrus" 28 "golang.org/x/net/context" 29 ) 30 31 type invalidIsolationError string 32 33 func (e invalidIsolationError) Error() string { 34 return fmt.Sprintf("Unsupported isolation: %q", string(e)) 35 } 36 37 func (e invalidIsolationError) InvalidParameter() {} 38 39 func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBuildOptions, error) { 40 version := httputils.VersionFromContext(ctx) 41 options := &types.ImageBuildOptions{} 42 if httputils.BoolValue(r, "forcerm") && versions.GreaterThanOrEqualTo(version, "1.12") { 43 options.Remove = true 44 } else if r.FormValue("rm") == "" && versions.GreaterThanOrEqualTo(version, "1.12") { 45 options.Remove = true 46 } else { 47 options.Remove = httputils.BoolValue(r, "rm") 48 } 49 if httputils.BoolValue(r, "pull") && versions.GreaterThanOrEqualTo(version, "1.16") { 50 options.PullParent = true 51 } 52 53 options.Dockerfile = r.FormValue("dockerfile") 54 options.SuppressOutput = httputils.BoolValue(r, "q") 55 options.NoCache = httputils.BoolValue(r, "nocache") 56 options.ForceRemove = httputils.BoolValue(r, "forcerm") 57 options.MemorySwap = httputils.Int64ValueOrZero(r, "memswap") 58 options.Memory = httputils.Int64ValueOrZero(r, "memory") 59 options.CPUShares = httputils.Int64ValueOrZero(r, "cpushares") 60 options.CPUPeriod = httputils.Int64ValueOrZero(r, "cpuperiod") 61 options.CPUQuota = httputils.Int64ValueOrZero(r, "cpuquota") 62 options.CPUSetCPUs = r.FormValue("cpusetcpus") 63 options.CPUSetMems = r.FormValue("cpusetmems") 64 options.CgroupParent = r.FormValue("cgroupparent") 65 options.NetworkMode = r.FormValue("networkmode") 66 options.Tags = r.Form["t"] 67 options.ExtraHosts = r.Form["extrahosts"] 68 options.SecurityOpt = r.Form["securityopt"] 69 options.Squash = httputils.BoolValue(r, "squash") 70 options.Target = r.FormValue("target") 71 options.RemoteContext = r.FormValue("remote") 72 if versions.GreaterThanOrEqualTo(version, "1.32") { 73 apiPlatform := r.FormValue("platform") 74 p := system.ParsePlatform(apiPlatform) 75 if err := system.ValidatePlatform(p); err != nil { 76 return nil, errdefs.InvalidParameter(errors.Errorf("invalid platform: %s", err)) 77 } 78 options.Platform = p.OS 79 } 80 81 if r.Form.Get("shmsize") != "" { 82 shmSize, err := strconv.ParseInt(r.Form.Get("shmsize"), 10, 64) 83 if err != nil { 84 return nil, err 85 } 86 options.ShmSize = shmSize 87 } 88 89 if i := container.Isolation(r.FormValue("isolation")); i != "" { 90 if !container.Isolation.IsValid(i) { 91 return nil, invalidIsolationError(i) 92 } 93 options.Isolation = i 94 } 95 96 if runtime.GOOS != "windows" && options.SecurityOpt != nil { 97 return nil, errdefs.InvalidParameter(errors.New("The daemon on this platform does not support setting security options on build")) 98 } 99 100 var buildUlimits = []*units.Ulimit{} 101 ulimitsJSON := r.FormValue("ulimits") 102 if ulimitsJSON != "" { 103 if err := json.Unmarshal([]byte(ulimitsJSON), &buildUlimits); err != nil { 104 return nil, errors.Wrap(errdefs.InvalidParameter(err), "error reading ulimit settings") 105 } 106 options.Ulimits = buildUlimits 107 } 108 109 // Note that there are two ways a --build-arg might appear in the 110 // json of the query param: 111 // "foo":"bar" 112 // and "foo":nil 113 // The first is the normal case, ie. --build-arg foo=bar 114 // or --build-arg foo 115 // where foo's value was picked up from an env var. 116 // The second ("foo":nil) is where they put --build-arg foo 117 // but "foo" isn't set as an env var. In that case we can't just drop 118 // the fact they mentioned it, we need to pass that along to the builder 119 // so that it can print a warning about "foo" being unused if there is 120 // no "ARG foo" in the Dockerfile. 121 buildArgsJSON := r.FormValue("buildargs") 122 if buildArgsJSON != "" { 123 var buildArgs = map[string]*string{} 124 if err := json.Unmarshal([]byte(buildArgsJSON), &buildArgs); err != nil { 125 return nil, errors.Wrap(errdefs.InvalidParameter(err), "error reading build args") 126 } 127 options.BuildArgs = buildArgs 128 } 129 130 labelsJSON := r.FormValue("labels") 131 if labelsJSON != "" { 132 var labels = map[string]string{} 133 if err := json.Unmarshal([]byte(labelsJSON), &labels); err != nil { 134 return nil, errors.Wrap(errdefs.InvalidParameter(err), "error reading labels") 135 } 136 options.Labels = labels 137 } 138 139 cacheFromJSON := r.FormValue("cachefrom") 140 if cacheFromJSON != "" { 141 var cacheFrom = []string{} 142 if err := json.Unmarshal([]byte(cacheFromJSON), &cacheFrom); err != nil { 143 return nil, err 144 } 145 options.CacheFrom = cacheFrom 146 } 147 options.SessionID = r.FormValue("session") 148 149 return options, nil 150 } 151 152 func (br *buildRouter) postPrune(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 153 report, err := br.backend.PruneCache(ctx) 154 if err != nil { 155 return err 156 } 157 return httputils.WriteJSON(w, http.StatusOK, report) 158 } 159 160 func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { 161 var ( 162 notVerboseBuffer = bytes.NewBuffer(nil) 163 version = httputils.VersionFromContext(ctx) 164 ) 165 166 w.Header().Set("Content-Type", "application/json") 167 168 output := ioutils.NewWriteFlusher(w) 169 defer output.Close() 170 errf := func(err error) error { 171 if httputils.BoolValue(r, "q") && notVerboseBuffer.Len() > 0 { 172 output.Write(notVerboseBuffer.Bytes()) 173 } 174 // Do not write the error in the http output if it's still empty. 175 // This prevents from writing a 200(OK) when there is an internal error. 176 if !output.Flushed() { 177 return err 178 } 179 _, err = w.Write(streamformatter.FormatError(err)) 180 if err != nil { 181 logrus.Warnf("could not write error response: %v", err) 182 } 183 return nil 184 } 185 186 buildOptions, err := newImageBuildOptions(ctx, r) 187 if err != nil { 188 return errf(err) 189 } 190 buildOptions.AuthConfigs = getAuthConfigs(r.Header) 191 192 if buildOptions.Squash && !br.daemon.HasExperimental() { 193 return errdefs.InvalidParameter(errors.New("squash is only supported with experimental mode")) 194 } 195 196 out := io.Writer(output) 197 if buildOptions.SuppressOutput { 198 out = notVerboseBuffer 199 } 200 201 // Currently, only used if context is from a remote url. 202 // Look at code in DetectContextFromRemoteURL for more information. 203 createProgressReader := func(in io.ReadCloser) io.ReadCloser { 204 progressOutput := streamformatter.NewJSONProgressOutput(out, true) 205 return progress.NewProgressReader(in, progressOutput, r.ContentLength, "Downloading context", buildOptions.RemoteContext) 206 } 207 208 wantAux := versions.GreaterThanOrEqualTo(version, "1.30") 209 210 imgID, err := br.backend.Build(ctx, backend.BuildConfig{ 211 Source: r.Body, 212 Options: buildOptions, 213 ProgressWriter: buildProgressWriter(out, wantAux, createProgressReader), 214 }) 215 if err != nil { 216 return errf(err) 217 } 218 219 // Everything worked so if -q was provided the output from the daemon 220 // should be just the image ID and we'll print that to stdout. 221 if buildOptions.SuppressOutput { 222 fmt.Fprintln(streamformatter.NewStdoutWriter(output), imgID) 223 } 224 return nil 225 } 226 227 func getAuthConfigs(header http.Header) map[string]types.AuthConfig { 228 authConfigs := map[string]types.AuthConfig{} 229 authConfigsEncoded := header.Get("X-Registry-Config") 230 231 if authConfigsEncoded == "" { 232 return authConfigs 233 } 234 235 authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded)) 236 // Pulling an image does not error when no auth is provided so to remain 237 // consistent with the existing api decode errors are ignored 238 json.NewDecoder(authConfigsJSON).Decode(&authConfigs) 239 return authConfigs 240 } 241 242 type syncWriter struct { 243 w io.Writer 244 mu sync.Mutex 245 } 246 247 func (s *syncWriter) Write(b []byte) (count int, err error) { 248 s.mu.Lock() 249 count, err = s.w.Write(b) 250 s.mu.Unlock() 251 return 252 } 253 254 func buildProgressWriter(out io.Writer, wantAux bool, createProgressReader func(io.ReadCloser) io.ReadCloser) backend.ProgressWriter { 255 out = &syncWriter{w: out} 256 257 var aux *streamformatter.AuxFormatter 258 if wantAux { 259 aux = &streamformatter.AuxFormatter{Writer: out} 260 } 261 262 return backend.ProgressWriter{ 263 Output: out, 264 StdoutFormatter: streamformatter.NewStdoutWriter(out), 265 StderrFormatter: streamformatter.NewStderrWriter(out), 266 AuxFormatter: aux, 267 ProgressReaderFunc: createProgressReader, 268 } 269 }