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