Skip to content

Commit 535c4c9

Browse files
committed
Implement docker build with standalone client lib.
Signed-off-by: David Calavera <david.calavera@gmail.com>
1 parent 900ad28 commit 535c4c9

File tree

2 files changed

+190
-98
lines changed

2 files changed

+190
-98
lines changed

api/client/build.go

Lines changed: 40 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,19 @@ package client
33
import (
44
"archive/tar"
55
"bufio"
6-
"encoding/base64"
7-
"encoding/json"
86
"fmt"
97
"io"
108
"io/ioutil"
11-
"net/http"
12-
"net/url"
139
"os"
1410
"os/exec"
1511
"path/filepath"
1612
"regexp"
1713
"runtime"
18-
"strconv"
1914
"strings"
2015

2116
"github.com/docker/distribution/reference"
2217
"github.com/docker/docker/api"
18+
"github.com/docker/docker/api/client/lib"
2319
Cli "github.com/docker/docker/cli"
2420
"github.com/docker/docker/opts"
2521
"github.com/docker/docker/pkg/archive"
@@ -33,7 +29,6 @@ import (
3329
"github.com/docker/docker/pkg/units"
3430
"github.com/docker/docker/pkg/urlutil"
3531
"github.com/docker/docker/registry"
36-
"github.com/docker/docker/runconfig"
3732
tagpkg "github.com/docker/docker/tag"
3833
"github.com/docker/docker/utils"
3934
)
@@ -207,108 +202,55 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
207202
}
208203
}
209204

210-
// Send the build context
211-
v := url.Values{
212-
"t": flTags.GetAll(),
213-
}
214-
if *suppressOutput {
215-
v.Set("q", "1")
216-
}
205+
var remoteContext string
217206
if isRemote {
218-
v.Set("remote", cmd.Arg(0))
219-
}
220-
if *noCache {
221-
v.Set("nocache", "1")
222-
}
223-
if *rm {
224-
v.Set("rm", "1")
225-
} else {
226-
v.Set("rm", "0")
227-
}
228-
229-
if *forceRm {
230-
v.Set("forcerm", "1")
231-
}
232-
233-
if *pull {
234-
v.Set("pull", "1")
235-
}
236-
237-
if !runconfig.IsolationLevel.IsDefault(runconfig.IsolationLevel(*isolation)) {
238-
v.Set("isolation", *isolation)
239-
}
240-
241-
v.Set("cpusetcpus", *flCPUSetCpus)
242-
v.Set("cpusetmems", *flCPUSetMems)
243-
v.Set("cpushares", strconv.FormatInt(*flCPUShares, 10))
244-
v.Set("cpuquota", strconv.FormatInt(*flCPUQuota, 10))
245-
v.Set("cpuperiod", strconv.FormatInt(*flCPUPeriod, 10))
246-
v.Set("memory", strconv.FormatInt(memory, 10))
247-
v.Set("memswap", strconv.FormatInt(memorySwap, 10))
248-
v.Set("cgroupparent", *flCgroupParent)
249-
250-
if *flShmSize != "" {
251-
parsedShmSize, err := units.RAMInBytes(*flShmSize)
252-
if err != nil {
253-
return err
254-
}
255-
v.Set("shmsize", strconv.FormatInt(parsedShmSize, 10))
256-
}
257-
258-
v.Set("dockerfile", relDockerfile)
259-
260-
ulimitsVar := flUlimits.GetList()
261-
ulimitsJSON, err := json.Marshal(ulimitsVar)
262-
if err != nil {
263-
return err
264-
}
265-
v.Set("ulimits", string(ulimitsJSON))
266-
267-
// collect all the build-time environment variables for the container
268-
buildArgs := runconfig.ConvertKVStringsToMap(flBuildArg.GetAll())
269-
buildArgsJSON, err := json.Marshal(buildArgs)
207+
remoteContext = cmd.Arg(0)
208+
}
209+
210+
options := lib.ImageBuildOptions{
211+
Context: body,
212+
Memory: memory,
213+
MemorySwap: memorySwap,
214+
Tags: flTags.GetAll(),
215+
SuppressOutput: *suppressOutput,
216+
RemoteContext: remoteContext,
217+
NoCache: *noCache,
218+
Remove: *rm,
219+
ForceRemove: *forceRm,
220+
PullParent: *pull,
221+
Isolation: *isolation,
222+
CPUSetCPUs: *flCPUSetCpus,
223+
CPUSetMems: *flCPUSetMems,
224+
CPUShares: *flCPUShares,
225+
CPUQuota: *flCPUQuota,
226+
CPUPeriod: *flCPUPeriod,
227+
CgroupParent: *flCgroupParent,
228+
ShmSize: *flShmSize,
229+
Dockerfile: relDockerfile,
230+
Ulimits: flUlimits.GetList(),
231+
BuildArgs: flBuildArg.GetAll(),
232+
AuthConfigs: cli.configFile.AuthConfigs,
233+
}
234+
235+
response, err := cli.client.ImageBuild(options)
270236
if err != nil {
271237
return err
272238
}
273-
v.Set("buildargs", string(buildArgsJSON))
274239

275-
headers := http.Header(make(map[string][]string))
276-
buf, err := json.Marshal(cli.configFile.AuthConfigs)
240+
err = jsonmessage.DisplayJSONMessagesStream(response.Body, cli.out, cli.outFd, cli.isTerminalOut)
277241
if err != nil {
278-
return err
279-
}
280-
headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
281-
headers.Set("Content-Type", "application/tar")
282-
283-
sopts := &streamOpts{
284-
rawTerminal: true,
285-
in: body,
286-
out: cli.out,
287-
headers: headers,
288-
}
289-
290-
serverResp, err := cli.stream("POST", fmt.Sprintf("/build?%s", v.Encode()), sopts)
291-
292-
// Windows: show error message about modified file permissions.
293-
if runtime.GOOS == "windows" {
294-
h, err := httputils.ParseServerHeader(serverResp.header.Get("Server"))
295-
if err == nil {
296-
if h.OS != "windows" {
297-
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.`)
242+
if jerr, ok := err.(*jsonmessage.JSONError); ok {
243+
// If no error code is set, default to 1
244+
if jerr.Code == 0 {
245+
jerr.Code = 1
298246
}
247+
return Cli.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
299248
}
300249
}
301250

302-
if jerr, ok := err.(*jsonmessage.JSONError); ok {
303-
// If no error code is set, default to 1
304-
if jerr.Code == 0 {
305-
jerr.Code = 1
306-
}
307-
return Cli.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
308-
}
309-
310-
if err != nil {
311-
return err
251+
// Windows: show error message about modified file permissions.
252+
if response.OSType == "windows" {
253+
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.`)
312254
}
313255

314256
// Since the build was successful, now we must tag any of the resolved

api/client/lib/image_build.go

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package lib
2+
3+
import (
4+
"encoding/base64"
5+
"encoding/json"
6+
"io"
7+
"net/http"
8+
"net/url"
9+
"strconv"
10+
11+
"github.com/docker/docker/cliconfig"
12+
"github.com/docker/docker/pkg/httputils"
13+
"github.com/docker/docker/pkg/ulimit"
14+
"github.com/docker/docker/pkg/units"
15+
"github.com/docker/docker/runconfig"
16+
)
17+
18+
// ImageBuildOptions holds the information
19+
// necessary to build images.
20+
type ImageBuildOptions struct {
21+
Tags []string
22+
SuppressOutput bool
23+
RemoteContext string
24+
NoCache bool
25+
Remove bool
26+
ForceRemove bool
27+
PullParent bool
28+
Isolation string
29+
CPUSetCPUs string
30+
CPUSetMems string
31+
CPUShares int64
32+
CPUQuota int64
33+
CPUPeriod int64
34+
Memory int64
35+
MemorySwap int64
36+
CgroupParent string
37+
ShmSize string
38+
Dockerfile string
39+
Ulimits []*ulimit.Ulimit
40+
BuildArgs []string
41+
AuthConfigs map[string]cliconfig.AuthConfig
42+
Context io.Reader
43+
}
44+
45+
// ImageBuildResponse holds information
46+
// returned by a server after building
47+
// an image.
48+
type ImageBuildResponse struct {
49+
Body io.ReadCloser
50+
OSType string
51+
}
52+
53+
// ImageBuild sends request to the daemon to build images.
54+
// The Body in the response implement an io.ReadCloser and it's up to the caller to
55+
// close it.
56+
func (cli *Client) ImageBuild(options ImageBuildOptions) (ImageBuildResponse, error) {
57+
query, err := imageBuildOptionsToQuery(options)
58+
if err != nil {
59+
return ImageBuildResponse{}, err
60+
}
61+
62+
headers := http.Header(make(map[string][]string))
63+
buf, err := json.Marshal(options.AuthConfigs)
64+
if err != nil {
65+
return ImageBuildResponse{}, err
66+
}
67+
headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
68+
headers.Set("Content-Type", "application/tar")
69+
70+
serverResp, err := cli.POSTRaw("/build", query, options.Context, headers)
71+
if err != nil {
72+
return ImageBuildResponse{}, err
73+
}
74+
75+
var osType string
76+
if h, err := httputils.ParseServerHeader(serverResp.header.Get("Server")); err == nil {
77+
osType = h.OS
78+
}
79+
80+
return ImageBuildResponse{
81+
Body: serverResp.body,
82+
OSType: osType,
83+
}, nil
84+
}
85+
86+
func imageBuildOptionsToQuery(options ImageBuildOptions) (url.Values, error) {
87+
query := url.Values{
88+
"t": options.Tags,
89+
}
90+
if options.SuppressOutput {
91+
query.Set("q", "1")
92+
}
93+
if options.RemoteContext != "" {
94+
query.Set("remote", options.RemoteContext)
95+
}
96+
if options.NoCache {
97+
query.Set("nocache", "1")
98+
}
99+
if options.Remove {
100+
query.Set("rm", "1")
101+
} else {
102+
query.Set("rm", "0")
103+
}
104+
105+
if options.ForceRemove {
106+
query.Set("forcerm", "1")
107+
}
108+
109+
if options.PullParent {
110+
query.Set("pull", "1")
111+
}
112+
113+
if !runconfig.IsolationLevel.IsDefault(runconfig.IsolationLevel(options.Isolation)) {
114+
query.Set("isolation", options.Isolation)
115+
}
116+
117+
query.Set("cpusetcpus", options.CPUSetCPUs)
118+
query.Set("cpusetmems", options.CPUSetMems)
119+
query.Set("cpushares", strconv.FormatInt(options.CPUShares, 10))
120+
query.Set("cpuquota", strconv.FormatInt(options.CPUQuota, 10))
121+
query.Set("cpuperiod", strconv.FormatInt(options.CPUPeriod, 10))
122+
query.Set("memory", strconv.FormatInt(options.Memory, 10))
123+
query.Set("memswap", strconv.FormatInt(options.MemorySwap, 10))
124+
query.Set("cgroupparent", options.CgroupParent)
125+
126+
if options.ShmSize != "" {
127+
parsedShmSize, err := units.RAMInBytes(options.ShmSize)
128+
if err != nil {
129+
return query, err
130+
}
131+
query.Set("shmsize", strconv.FormatInt(parsedShmSize, 10))
132+
}
133+
134+
query.Set("dockerfile", options.Dockerfile)
135+
136+
ulimitsJSON, err := json.Marshal(options.Ulimits)
137+
if err != nil {
138+
return query, err
139+
}
140+
query.Set("ulimits", string(ulimitsJSON))
141+
142+
buildArgs := runconfig.ConvertKVStringsToMap(options.BuildArgs)
143+
buildArgsJSON, err := json.Marshal(buildArgs)
144+
if err != nil {
145+
return query, err
146+
}
147+
query.Set("buildargs", string(buildArgsJSON))
148+
149+
return query, nil
150+
}

0 commit comments

Comments
 (0)