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