Преглед на файлове

Implement docker build with standalone client lib.

Signed-off-by: David Calavera <david.calavera@gmail.com>
David Calavera преди 9 години
родител
ревизия
535c4c9a59
променени са 2 файла, в които са добавени 190 реда и са изтрити 98 реда
  1. 40 98
      api/client/build.go
  2. 150 0
      api/client/lib/image_build.go

+ 40 - 98
api/client/build.go

@@ -3,23 +3,19 @@ package client
 import (
 	"archive/tar"
 	"bufio"
-	"encoding/base64"
-	"encoding/json"
 	"fmt"
 	"io"
 	"io/ioutil"
-	"net/http"
-	"net/url"
 	"os"
 	"os/exec"
 	"path/filepath"
 	"regexp"
 	"runtime"
-	"strconv"
 	"strings"
 
 	"github.com/docker/distribution/reference"
 	"github.com/docker/docker/api"
+	"github.com/docker/docker/api/client/lib"
 	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/pkg/archive"
@@ -33,7 +29,6 @@ import (
 	"github.com/docker/docker/pkg/units"
 	"github.com/docker/docker/pkg/urlutil"
 	"github.com/docker/docker/registry"
-	"github.com/docker/docker/runconfig"
 	tagpkg "github.com/docker/docker/tag"
 	"github.com/docker/docker/utils"
 )
@@ -207,108 +202,55 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 		}
 	}
 
-	// Send the build context
-	v := url.Values{
-		"t": flTags.GetAll(),
-	}
-	if *suppressOutput {
-		v.Set("q", "1")
-	}
+	var remoteContext string
 	if isRemote {
-		v.Set("remote", cmd.Arg(0))
-	}
-	if *noCache {
-		v.Set("nocache", "1")
-	}
-	if *rm {
-		v.Set("rm", "1")
-	} else {
-		v.Set("rm", "0")
-	}
-
-	if *forceRm {
-		v.Set("forcerm", "1")
-	}
-
-	if *pull {
-		v.Set("pull", "1")
-	}
-
-	if !runconfig.IsolationLevel.IsDefault(runconfig.IsolationLevel(*isolation)) {
-		v.Set("isolation", *isolation)
-	}
-
-	v.Set("cpusetcpus", *flCPUSetCpus)
-	v.Set("cpusetmems", *flCPUSetMems)
-	v.Set("cpushares", strconv.FormatInt(*flCPUShares, 10))
-	v.Set("cpuquota", strconv.FormatInt(*flCPUQuota, 10))
-	v.Set("cpuperiod", strconv.FormatInt(*flCPUPeriod, 10))
-	v.Set("memory", strconv.FormatInt(memory, 10))
-	v.Set("memswap", strconv.FormatInt(memorySwap, 10))
-	v.Set("cgroupparent", *flCgroupParent)
-
-	if *flShmSize != "" {
-		parsedShmSize, err := units.RAMInBytes(*flShmSize)
-		if err != nil {
-			return err
-		}
-		v.Set("shmsize", strconv.FormatInt(parsedShmSize, 10))
-	}
-
-	v.Set("dockerfile", relDockerfile)
-
-	ulimitsVar := flUlimits.GetList()
-	ulimitsJSON, err := json.Marshal(ulimitsVar)
-	if err != nil {
-		return err
-	}
-	v.Set("ulimits", string(ulimitsJSON))
-
-	// collect all the build-time environment variables for the container
-	buildArgs := runconfig.ConvertKVStringsToMap(flBuildArg.GetAll())
-	buildArgsJSON, err := json.Marshal(buildArgs)
+		remoteContext = cmd.Arg(0)
+	}
+
+	options := lib.ImageBuildOptions{
+		Context:        body,
+		Memory:         memory,
+		MemorySwap:     memorySwap,
+		Tags:           flTags.GetAll(),
+		SuppressOutput: *suppressOutput,
+		RemoteContext:  remoteContext,
+		NoCache:        *noCache,
+		Remove:         *rm,
+		ForceRemove:    *forceRm,
+		PullParent:     *pull,
+		Isolation:      *isolation,
+		CPUSetCPUs:     *flCPUSetCpus,
+		CPUSetMems:     *flCPUSetMems,
+		CPUShares:      *flCPUShares,
+		CPUQuota:       *flCPUQuota,
+		CPUPeriod:      *flCPUPeriod,
+		CgroupParent:   *flCgroupParent,
+		ShmSize:        *flShmSize,
+		Dockerfile:     relDockerfile,
+		Ulimits:        flUlimits.GetList(),
+		BuildArgs:      flBuildArg.GetAll(),
+		AuthConfigs:    cli.configFile.AuthConfigs,
+	}
+
+	response, err := cli.client.ImageBuild(options)
 	if err != nil {
 		return err
 	}
-	v.Set("buildargs", string(buildArgsJSON))
 
-	headers := http.Header(make(map[string][]string))
-	buf, err := json.Marshal(cli.configFile.AuthConfigs)
+	err = jsonmessage.DisplayJSONMessagesStream(response.Body, cli.out, cli.outFd, cli.isTerminalOut)
 	if err != nil {
-		return err
-	}
-	headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
-	headers.Set("Content-Type", "application/tar")
-
-	sopts := &streamOpts{
-		rawTerminal: true,
-		in:          body,
-		out:         cli.out,
-		headers:     headers,
-	}
-
-	serverResp, err := cli.stream("POST", fmt.Sprintf("/build?%s", v.Encode()), sopts)
-
-	// Windows: show error message about modified file permissions.
-	if runtime.GOOS == "windows" {
-		h, err := httputils.ParseServerHeader(serverResp.header.Get("Server"))
-		if err == nil {
-			if h.OS != "windows" {
-				fmt.Fprintln(cli.err, `SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.`)
+		if jerr, ok := err.(*jsonmessage.JSONError); ok {
+			// If no error code is set, default to 1
+			if jerr.Code == 0 {
+				jerr.Code = 1
 			}
+			return Cli.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
 		}
 	}
 
-	if jerr, ok := err.(*jsonmessage.JSONError); ok {
-		// If no error code is set, default to 1
-		if jerr.Code == 0 {
-			jerr.Code = 1
-		}
-		return Cli.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
-	}
-
-	if err != nil {
-		return err
+	// Windows: show error message about modified file permissions.
+	if response.OSType == "windows" {
+		fmt.Fprintln(cli.err, `SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories.`)
 	}
 
 	// Since the build was successful, now we must tag any of the resolved

+ 150 - 0
api/client/lib/image_build.go

@@ -0,0 +1,150 @@
+package lib
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"io"
+	"net/http"
+	"net/url"
+	"strconv"
+
+	"github.com/docker/docker/cliconfig"
+	"github.com/docker/docker/pkg/httputils"
+	"github.com/docker/docker/pkg/ulimit"
+	"github.com/docker/docker/pkg/units"
+	"github.com/docker/docker/runconfig"
+)
+
+// ImageBuildOptions holds the information
+// necessary to build images.
+type ImageBuildOptions struct {
+	Tags           []string
+	SuppressOutput bool
+	RemoteContext  string
+	NoCache        bool
+	Remove         bool
+	ForceRemove    bool
+	PullParent     bool
+	Isolation      string
+	CPUSetCPUs     string
+	CPUSetMems     string
+	CPUShares      int64
+	CPUQuota       int64
+	CPUPeriod      int64
+	Memory         int64
+	MemorySwap     int64
+	CgroupParent   string
+	ShmSize        string
+	Dockerfile     string
+	Ulimits        []*ulimit.Ulimit
+	BuildArgs      []string
+	AuthConfigs    map[string]cliconfig.AuthConfig
+	Context        io.Reader
+}
+
+// ImageBuildResponse holds information
+// returned by a server after building
+// an image.
+type ImageBuildResponse struct {
+	Body   io.ReadCloser
+	OSType string
+}
+
+// ImageBuild sends request to the daemon to build images.
+// The Body in the response implement an io.ReadCloser and it's up to the caller to
+// close it.
+func (cli *Client) ImageBuild(options ImageBuildOptions) (ImageBuildResponse, error) {
+	query, err := imageBuildOptionsToQuery(options)
+	if err != nil {
+		return ImageBuildResponse{}, err
+	}
+
+	headers := http.Header(make(map[string][]string))
+	buf, err := json.Marshal(options.AuthConfigs)
+	if err != nil {
+		return ImageBuildResponse{}, err
+	}
+	headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
+	headers.Set("Content-Type", "application/tar")
+
+	serverResp, err := cli.POSTRaw("/build", query, options.Context, headers)
+	if err != nil {
+		return ImageBuildResponse{}, err
+	}
+
+	var osType string
+	if h, err := httputils.ParseServerHeader(serverResp.header.Get("Server")); err == nil {
+		osType = h.OS
+	}
+
+	return ImageBuildResponse{
+		Body:   serverResp.body,
+		OSType: osType,
+	}, nil
+}
+
+func imageBuildOptionsToQuery(options ImageBuildOptions) (url.Values, error) {
+	query := url.Values{
+		"t": options.Tags,
+	}
+	if options.SuppressOutput {
+		query.Set("q", "1")
+	}
+	if options.RemoteContext != "" {
+		query.Set("remote", options.RemoteContext)
+	}
+	if options.NoCache {
+		query.Set("nocache", "1")
+	}
+	if options.Remove {
+		query.Set("rm", "1")
+	} else {
+		query.Set("rm", "0")
+	}
+
+	if options.ForceRemove {
+		query.Set("forcerm", "1")
+	}
+
+	if options.PullParent {
+		query.Set("pull", "1")
+	}
+
+	if !runconfig.IsolationLevel.IsDefault(runconfig.IsolationLevel(options.Isolation)) {
+		query.Set("isolation", options.Isolation)
+	}
+
+	query.Set("cpusetcpus", options.CPUSetCPUs)
+	query.Set("cpusetmems", options.CPUSetMems)
+	query.Set("cpushares", strconv.FormatInt(options.CPUShares, 10))
+	query.Set("cpuquota", strconv.FormatInt(options.CPUQuota, 10))
+	query.Set("cpuperiod", strconv.FormatInt(options.CPUPeriod, 10))
+	query.Set("memory", strconv.FormatInt(options.Memory, 10))
+	query.Set("memswap", strconv.FormatInt(options.MemorySwap, 10))
+	query.Set("cgroupparent", options.CgroupParent)
+
+	if options.ShmSize != "" {
+		parsedShmSize, err := units.RAMInBytes(options.ShmSize)
+		if err != nil {
+			return query, err
+		}
+		query.Set("shmsize", strconv.FormatInt(parsedShmSize, 10))
+	}
+
+	query.Set("dockerfile", options.Dockerfile)
+
+	ulimitsJSON, err := json.Marshal(options.Ulimits)
+	if err != nil {
+		return query, err
+	}
+	query.Set("ulimits", string(ulimitsJSON))
+
+	buildArgs := runconfig.ConvertKVStringsToMap(options.BuildArgs)
+	buildArgsJSON, err := json.Marshal(buildArgs)
+	if err != nil {
+		return query, err
+	}
+	query.Set("buildargs", string(buildArgsJSON))
+
+	return query, nil
+}