github.com/kim0/docker@v0.6.2-0.20161130212042-4addda3f07e7/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  
    58  	if r.Form.Get("shmsize") != "" {
    59  		shmSize, err := strconv.ParseInt(r.Form.Get("shmsize"), 10, 64)
    60  		if err != nil {
    61  			return nil, err
    62  		}
    63  		options.ShmSize = shmSize
    64  	}
    65  
    66  	if i := container.Isolation(r.FormValue("isolation")); i != "" {
    67  		if !container.Isolation.IsValid(i) {
    68  			return nil, fmt.Errorf("Unsupported isolation: %q", i)
    69  		}
    70  		options.Isolation = i
    71  	}
    72  
    73  	if runtime.GOOS != "windows" && options.SecurityOpt != nil {
    74  		return nil, fmt.Errorf("the daemon on this platform does not support --security-opt to build")
    75  	}
    76  
    77  	var buildUlimits = []*units.Ulimit{}
    78  	ulimitsJSON := r.FormValue("ulimits")
    79  	if ulimitsJSON != "" {
    80  		if err := json.Unmarshal([]byte(ulimitsJSON), &buildUlimits); err != nil {
    81  			return nil, err
    82  		}
    83  		options.Ulimits = buildUlimits
    84  	}
    85  
    86  	var buildArgs = map[string]string{}
    87  	buildArgsJSON := r.FormValue("buildargs")
    88  	if buildArgsJSON != "" {
    89  		if err := json.Unmarshal([]byte(buildArgsJSON), &buildArgs); err != nil {
    90  			return nil, err
    91  		}
    92  		options.BuildArgs = buildArgs
    93  	}
    94  	var labels = map[string]string{}
    95  	labelsJSON := r.FormValue("labels")
    96  	if labelsJSON != "" {
    97  		if err := json.Unmarshal([]byte(labelsJSON), &labels); err != nil {
    98  			return nil, err
    99  		}
   100  		options.Labels = labels
   101  	}
   102  
   103  	var cacheFrom = []string{}
   104  	cacheFromJSON := r.FormValue("cachefrom")
   105  	if cacheFromJSON != "" {
   106  		if err := json.Unmarshal([]byte(cacheFromJSON), &cacheFrom); err != nil {
   107  			return nil, err
   108  		}
   109  		options.CacheFrom = cacheFrom
   110  	}
   111  
   112  	return options, nil
   113  }
   114  
   115  type syncWriter struct {
   116  	w  io.Writer
   117  	mu sync.Mutex
   118  }
   119  
   120  func (s *syncWriter) Write(b []byte) (count int, err error) {
   121  	s.mu.Lock()
   122  	count, err = s.w.Write(b)
   123  	s.mu.Unlock()
   124  	return
   125  }
   126  
   127  func (br *buildRouter) postBuild(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   128  	var (
   129  		authConfigs        = map[string]types.AuthConfig{}
   130  		authConfigsEncoded = r.Header.Get("X-Registry-Config")
   131  		notVerboseBuffer   = bytes.NewBuffer(nil)
   132  	)
   133  
   134  	if authConfigsEncoded != "" {
   135  		authConfigsJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authConfigsEncoded))
   136  		if err := json.NewDecoder(authConfigsJSON).Decode(&authConfigs); err != nil {
   137  			// for a pull it is not an error if no auth was given
   138  			// to increase compatibility with the existing api it is defaulting
   139  			// to be empty.
   140  		}
   141  	}
   142  
   143  	w.Header().Set("Content-Type", "application/json")
   144  
   145  	output := ioutils.NewWriteFlusher(w)
   146  	defer output.Close()
   147  	sf := streamformatter.NewJSONStreamFormatter()
   148  	errf := func(err error) error {
   149  		if httputils.BoolValue(r, "q") && notVerboseBuffer.Len() > 0 {
   150  			output.Write(notVerboseBuffer.Bytes())
   151  		}
   152  		// Do not write the error in the http output if it's still empty.
   153  		// This prevents from writing a 200(OK) when there is an internal error.
   154  		if !output.Flushed() {
   155  			return err
   156  		}
   157  		_, err = w.Write(sf.FormatError(err))
   158  		if err != nil {
   159  			logrus.Warnf("could not write error response: %v", err)
   160  		}
   161  		return nil
   162  	}
   163  
   164  	buildOptions, err := newImageBuildOptions(ctx, r)
   165  	if err != nil {
   166  		return errf(err)
   167  	}
   168  	buildOptions.AuthConfigs = authConfigs
   169  
   170  	remoteURL := r.FormValue("remote")
   171  
   172  	// Currently, only used if context is from a remote url.
   173  	// Look at code in DetectContextFromRemoteURL for more information.
   174  	createProgressReader := func(in io.ReadCloser) io.ReadCloser {
   175  		progressOutput := sf.NewProgressOutput(output, true)
   176  		if buildOptions.SuppressOutput {
   177  			progressOutput = sf.NewProgressOutput(notVerboseBuffer, true)
   178  		}
   179  		return progress.NewProgressReader(in, progressOutput, r.ContentLength, "Downloading context", remoteURL)
   180  	}
   181  
   182  	out := io.Writer(output)
   183  	if buildOptions.SuppressOutput {
   184  		out = notVerboseBuffer
   185  	}
   186  	out = &syncWriter{w: out}
   187  	stdout := &streamformatter.StdoutFormatter{Writer: out, StreamFormatter: sf}
   188  	stderr := &streamformatter.StderrFormatter{Writer: out, StreamFormatter: sf}
   189  
   190  	pg := backend.ProgressWriter{
   191  		Output:             out,
   192  		StdoutFormatter:    stdout,
   193  		StderrFormatter:    stderr,
   194  		ProgressReaderFunc: createProgressReader,
   195  	}
   196  
   197  	imgID, err := br.backend.BuildFromContext(ctx, r.Body, remoteURL, buildOptions, pg)
   198  	if err != nil {
   199  		return errf(err)
   200  	}
   201  
   202  	// Everything worked so if -q was provided the output from the daemon
   203  	// should be just the image ID and we'll print that to stdout.
   204  	if buildOptions.SuppressOutput {
   205  		stdout := &streamformatter.StdoutFormatter{Writer: output, StreamFormatter: sf}
   206  		fmt.Fprintf(stdout, "%s\n", string(imgID))
   207  	}
   208  
   209  	return nil
   210  }