1
1
package docker
2
2
3
3
import (
4
- "archive/tar"
5
4
"bytes"
6
5
"context"
7
6
"io"
8
7
"io/ioutil"
9
8
"net"
10
9
"os"
10
+ "path/filepath"
11
11
"time"
12
12
13
13
"github.com/docker/buildx/driver"
14
14
"github.com/docker/buildx/driver/bkimage"
15
+ "github.com/docker/buildx/util/bkutil"
15
16
"github.com/docker/buildx/util/imagetools"
16
17
"github.com/docker/buildx/util/progress"
18
+ "github.com/docker/cli/cli/command"
17
19
"github.com/docker/docker/api/types"
18
20
dockertypes "github.com/docker/docker/api/types"
19
21
"github.com/docker/docker/api/types/container"
20
22
"github.com/docker/docker/api/types/mount"
21
23
"github.com/docker/docker/api/types/network"
22
24
dockerclient "github.com/docker/docker/client"
25
+ dockerarchive "github.com/docker/docker/pkg/archive"
23
26
"github.com/docker/docker/pkg/stdcopy"
27
+ "github.com/docker/docker/pkg/system"
24
28
"github.com/moby/buildkit/client"
25
29
"github.com/moby/buildkit/util/tracing/detect"
26
30
"github.com/pkg/errors"
27
31
)
28
32
29
33
const (
30
34
volumeStateSuffix = "_state"
31
-
32
- // containerStateDir is the location where buildkitd inside the container
33
- // stores its state. The container driver creates a Linux container, so
34
- // this should match the location for Linux, as defined in:
35
- // https://github.com/moby/buildkit/blob/v0.9.0/util/appdefaults/appdefaults_unix.go#L11-L15
36
- containerBuildKitRootDir = "/var/lib/buildkit"
37
35
)
38
36
39
37
type Driver struct {
@@ -119,7 +117,7 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error {
119
117
{
120
118
Type : mount .TypeVolume ,
121
119
Source : d .Name + volumeStateSuffix ,
122
- Target : containerBuildKitRootDir ,
120
+ Target : bkutil . ContainerBuildKitRootDir ,
123
121
},
124
122
},
125
123
}
@@ -139,11 +137,12 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error {
139
137
return err
140
138
}
141
139
if f := d .InitConfig .ConfigFile ; f != "" {
142
- buf , err := readFileToTar (f )
140
+ ctnConfigDir , err := bkutil . ContainerConfDir (f )
143
141
if err != nil {
144
142
return err
145
143
}
146
- if err := d .DockerAPI .CopyToContainer (ctx , d .Name , "/" , buf , dockertypes.CopyToContainerOptions {}); err != nil {
144
+ defer os .RemoveAll (ctnConfigDir )
145
+ if err := d .copyToContainer (ctx , ctnConfigDir + "/." , "/" ); err != nil {
147
146
return err
148
147
}
149
148
}
@@ -205,6 +204,89 @@ func (d *Driver) copyLogs(ctx context.Context, l progress.SubLogger) error {
205
204
return rc .Close ()
206
205
}
207
206
207
+ // copyToContainer is based on the implementation from docker/cli
208
+ // https://github.com/docker/cli/blob/master/cli/command/container/cp.go
209
+ func (d * Driver ) copyToContainer (ctx context.Context , srcPath string , dstPath string ) error {
210
+ var err error
211
+
212
+ // Get an absolute source path.
213
+ srcPath , err = resolveLocalPath (srcPath )
214
+ if err != nil {
215
+ return err
216
+ }
217
+
218
+ // Prepare the destination.
219
+ dstInfo := dockerarchive.CopyInfo {Path : dstPath }
220
+ dstStat , err := d .DockerAPI .ContainerStatPath (ctx , d .Name , dstPath )
221
+
222
+ // If the destination is a symbolic link, we should evaluate it.
223
+ if err == nil && dstStat .Mode & os .ModeSymlink != 0 {
224
+ linkTarget := dstStat .LinkTarget
225
+ if ! system .IsAbs (linkTarget ) {
226
+ dstParent , _ := dockerarchive .SplitPathDirEntry (dstPath )
227
+ linkTarget = filepath .Join (dstParent , linkTarget )
228
+ }
229
+ dstInfo .Path = linkTarget
230
+ if dstStat , err = d .DockerAPI .ContainerStatPath (ctx , d .Name , linkTarget ); err != nil {
231
+ return err
232
+ }
233
+ }
234
+
235
+ // Validate the destination path.
236
+ if err := command .ValidateOutputPathFileMode (dstStat .Mode ); err != nil {
237
+ return errors .Wrapf (err , `destination "%s:%s" must be a directory or a regular file` , d .Name , dstPath )
238
+ }
239
+
240
+ // Ignore any error and assume that the parent directory of the destination
241
+ // path exists, in which case the copy may still succeed. If there is any
242
+ // type of conflict (e.g., non-directory overwriting an existing directory
243
+ // or vice versa) the extraction will fail. If the destination simply did
244
+ // not exist, but the parent directory does, the extraction will still
245
+ // succeed.
246
+ dstInfo .Exists , dstInfo .IsDir = true , dstStat .Mode .IsDir ()
247
+
248
+ // Prepare source copy info.
249
+ srcInfo , err := dockerarchive .CopyInfoSourcePath (srcPath , true )
250
+ if err != nil {
251
+ return err
252
+ }
253
+
254
+ srcArchive , err := dockerarchive .TarResource (srcInfo )
255
+ if err != nil {
256
+ return err
257
+ }
258
+ defer srcArchive .Close ()
259
+
260
+ // With the stat info about the local source as well as the
261
+ // destination, we have enough information to know whether we need to
262
+ // alter the archive that we upload so that when the server extracts
263
+ // it to the specified directory in the container we get the desired
264
+ // copy behavior.
265
+
266
+ // See comments in the implementation of `dockerarchive.PrepareArchiveCopy`
267
+ // for exactly what goes into deciding how and whether the source
268
+ // archive needs to be altered for the correct copy behavior when it is
269
+ // extracted. This function also infers from the source and destination
270
+ // info which directory to extract to, which may be the parent of the
271
+ // destination that the user specified.
272
+ dstDir , preparedArchive , err := dockerarchive .PrepareArchiveCopy (srcArchive , srcInfo , dstInfo )
273
+ if err != nil {
274
+ return err
275
+ }
276
+ defer preparedArchive .Close ()
277
+
278
+ return d .DockerAPI .CopyToContainer (ctx , d .Name , dstDir , preparedArchive , dockertypes.CopyToContainerOptions {
279
+ AllowOverwriteDirWithFile : false ,
280
+ })
281
+ }
282
+
283
+ func resolveLocalPath (localPath string ) (absPath string , err error ) {
284
+ if absPath , err = filepath .Abs (localPath ); err != nil {
285
+ return
286
+ }
287
+ return dockerarchive .PreserveTrailingDotOrSeparator (absPath , localPath , filepath .Separator ), nil
288
+ }
289
+
208
290
func (d * Driver ) exec (ctx context.Context , cmd []string ) (string , net.Conn , error ) {
209
291
execConfig := types.ExecConfig {
210
292
Cmd : cmd ,
@@ -366,29 +448,6 @@ func (d *demux) Read(dt []byte) (int, error) {
366
448
return d .Reader .Read (dt )
367
449
}
368
450
369
- func readFileToTar (fn string ) (* bytes.Buffer , error ) {
370
- buf := bytes .NewBuffer (nil )
371
- tw := tar .NewWriter (buf )
372
- dt , err := ioutil .ReadFile (fn )
373
- if err != nil {
374
- return nil , err
375
- }
376
- if err := tw .WriteHeader (& tar.Header {
377
- Name : "/etc/buildkit/buildkitd.toml" ,
378
- Size : int64 (len (dt )),
379
- Mode : 0644 ,
380
- }); err != nil {
381
- return nil , err
382
- }
383
- if _ , err := tw .Write (dt ); err != nil {
384
- return nil , err
385
- }
386
- if err := tw .Close (); err != nil {
387
- return nil , err
388
- }
389
- return buf , nil
390
- }
391
-
392
451
type logWriter struct {
393
452
logger progress.SubLogger
394
453
stream int
0 commit comments