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