Implement docker build with standalone client lib.
Signed-off-by: David Calavera <david.calavera@gmail.com>
This commit is contained in:
parent
900ad2897f
commit
535c4c9a59
|
@ -3,23 +3,19 @@ package client
|
||||||
import (
|
import (
|
||||||
"archive/tar"
|
"archive/tar"
|
||||||
"bufio"
|
"bufio"
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/distribution/reference"
|
"github.com/docker/distribution/reference"
|
||||||
"github.com/docker/docker/api"
|
"github.com/docker/docker/api"
|
||||||
|
"github.com/docker/docker/api/client/lib"
|
||||||
Cli "github.com/docker/docker/cli"
|
Cli "github.com/docker/docker/cli"
|
||||||
"github.com/docker/docker/opts"
|
"github.com/docker/docker/opts"
|
||||||
"github.com/docker/docker/pkg/archive"
|
"github.com/docker/docker/pkg/archive"
|
||||||
|
@ -33,7 +29,6 @@ import (
|
||||||
"github.com/docker/docker/pkg/units"
|
"github.com/docker/docker/pkg/units"
|
||||||
"github.com/docker/docker/pkg/urlutil"
|
"github.com/docker/docker/pkg/urlutil"
|
||||||
"github.com/docker/docker/registry"
|
"github.com/docker/docker/registry"
|
||||||
"github.com/docker/docker/runconfig"
|
|
||||||
tagpkg "github.com/docker/docker/tag"
|
tagpkg "github.com/docker/docker/tag"
|
||||||
"github.com/docker/docker/utils"
|
"github.com/docker/docker/utils"
|
||||||
)
|
)
|
||||||
|
@ -207,108 +202,55 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the build context
|
var remoteContext string
|
||||||
v := url.Values{
|
|
||||||
"t": flTags.GetAll(),
|
|
||||||
}
|
|
||||||
if *suppressOutput {
|
|
||||||
v.Set("q", "1")
|
|
||||||
}
|
|
||||||
if isRemote {
|
if isRemote {
|
||||||
v.Set("remote", cmd.Arg(0))
|
remoteContext = cmd.Arg(0)
|
||||||
}
|
|
||||||
if *noCache {
|
|
||||||
v.Set("nocache", "1")
|
|
||||||
}
|
|
||||||
if *rm {
|
|
||||||
v.Set("rm", "1")
|
|
||||||
} else {
|
|
||||||
v.Set("rm", "0")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if *forceRm {
|
options := lib.ImageBuildOptions{
|
||||||
v.Set("forcerm", "1")
|
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
if *pull {
|
response, err := cli.client.ImageBuild(options)
|
||||||
v.Set("pull", "1")
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !runconfig.IsolationLevel.IsDefault(runconfig.IsolationLevel(*isolation)) {
|
err = jsonmessage.DisplayJSONMessagesStream(response.Body, cli.out, cli.outFd, cli.isTerminalOut)
|
||||||
v.Set("isolation", *isolation)
|
if err != nil {
|
||||||
}
|
if jerr, ok := err.(*jsonmessage.JSONError); ok {
|
||||||
|
// If no error code is set, default to 1
|
||||||
v.Set("cpusetcpus", *flCPUSetCpus)
|
if jerr.Code == 0 {
|
||||||
v.Set("cpusetmems", *flCPUSetMems)
|
jerr.Code = 1
|
||||||
v.Set("cpushares", strconv.FormatInt(*flCPUShares, 10))
|
}
|
||||||
v.Set("cpuquota", strconv.FormatInt(*flCPUQuota, 10))
|
return Cli.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
|
||||||
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)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v.Set("buildargs", string(buildArgsJSON))
|
|
||||||
|
|
||||||
headers := http.Header(make(map[string][]string))
|
|
||||||
buf, err := json.Marshal(cli.configFile.AuthConfigs)
|
|
||||||
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.
|
// Windows: show error message about modified file permissions.
|
||||||
if runtime.GOOS == "windows" {
|
if response.OSType == "windows" {
|
||||||
h, err := httputils.ParseServerHeader(serverResp.header.Get("Server"))
|
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 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 err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since the build was successful, now we must tag any of the resolved
|
// Since the build was successful, now we must tag any of the resolved
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
Loading…
Reference in New Issue