github.com/boynux/docker@v1.11.0-rc4/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/builder" 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 152 remoteURL := r.FormValue("remote") 153 154 // Currently, only used if context is from a remote url. 155 // Look at code in DetectContextFromRemoteURL for more information. 156 createProgressReader := func(in io.ReadCloser) io.ReadCloser { 157 progressOutput := sf.NewProgressOutput(output, true) 158 if buildOptions.SuppressOutput { 159 progressOutput = sf.NewProgressOutput(notVerboseBuffer, true) 160 } 161 return progress.NewProgressReader(in, progressOutput, r.ContentLength, "Downloading context", remoteURL) 162 } 163 164 var ( 165 context builder.ModifiableContext 166 dockerfileName string 167 out io.Writer 168 ) 169 context, dockerfileName, err = builder.DetectContextFromRemoteURL(r.Body, remoteURL, createProgressReader) 170 if err != nil { 171 return errf(err) 172 } 173 defer func() { 174 if err := context.Close(); err != nil { 175 logrus.Debugf("[BUILDER] failed to remove temporary context: %v", err) 176 } 177 }() 178 if len(dockerfileName) > 0 { 179 buildOptions.Dockerfile = dockerfileName 180 } 181 182 buildOptions.AuthConfigs = authConfigs 183 184 out = output 185 if buildOptions.SuppressOutput { 186 out = notVerboseBuffer 187 } 188 out = &syncWriter{w: out} 189 stdout := &streamformatter.StdoutFormatter{Writer: out, StreamFormatter: sf} 190 stderr := &streamformatter.StderrFormatter{Writer: out, StreamFormatter: sf} 191 192 closeNotifier := make(<-chan bool) 193 if notifier, ok := w.(http.CloseNotifier); ok { 194 closeNotifier = notifier.CloseNotify() 195 } 196 197 imgID, err := br.backend.Build(ctx, buildOptions, 198 builder.DockerIgnoreContext{ModifiableContext: context}, 199 stdout, stderr, out, 200 closeNotifier) 201 if err != nil { 202 return errf(err) 203 } 204 205 // Everything worked so if -q was provided the output from the daemon 206 // should be just the image ID and we'll print that to stdout. 207 if buildOptions.SuppressOutput { 208 stdout := &streamformatter.StdoutFormatter{Writer: output, StreamFormatter: sf} 209 fmt.Fprintf(stdout, "%s\n", string(imgID)) 210 } 211 212 return nil 213 }